Giter VIP home page Giter VIP logo

Comments (18)

zygoloid avatar zygoloid commented on July 20, 2024

For point 1, we will also need to specify the memory layout of std::contract_violation.

I do not think we should pursue point 2. Implementations can choose to use debug info to infer the caller location or similar.

However, I do think we should specify whether preconditions are evaluated in the caller or callee for a direct call, or perhaps specify two entry points so that an implementation can choose for itself. (One option: the primary symbol does not evaluate preconditions; a secondary vague linkage version exists to do that and tail call the primary one.)

from cxx-abi.

hamzasood avatar hamzasood commented on July 20, 2024

For point 1, we will also need to specify the memory layout of std::contract_violation.

Would that also involve specifying the layout for std::string_view? I suppose the violation object could instead store pointer/length pairs for each string.

I do not think we should pursue point 2. Implementations can choose to use debug info to infer the caller location or similar.

Fair enough. I was hoping for something more efficient but maybe that isn't needed.

One option: the primary symbol does not evaluate preconditions; a secondary vague linkage version exists to do that and tail call the primary one

That seems like the best approach to me; forcing a particular way will certainly hinder optimisations.

There're a few other unspecified things regarding linking together units with different build levels / continuation modes. Presumably some of those will need specifying as well?

from cxx-abi.

zygoloid avatar zygoloid commented on July 20, 2024

Would that also involve specifying the layout for std::string_view? I suppose the violation object could instead store pointer/length pairs for each string.

Yes, I think we can avoid specifying a string_view layout, either by specifying a layout or by requiring a private, constexpr contract_violation constructor with a certain signature.

There're a few other unspecified things regarding linking together units with different build levels / continuation modes. Presumably some of those will need specifying as well?

We need some mechanism by which the implementation provides the continuation mode and the violation handler. Strawman option: the translation unit that defines main provides a definition of __cxa_contract_violation, which calls the user-supplied violation handler and either returns or not (depending on the build mode). The ABI library provides a default, weak, implementation of __cxa_contract_violation that is used if main is built without contracts enabled.

If a translation unit is built with continuation mode off, but the translation unit providing __cxa_contract_violation is built with it on, then undefined behavior seems a reasonable result (the violation handler will return, but the call to it was likely a tail call). I don't think the ABI needs to talk about that; that seems to be up to the implementation.

If translation units are built with different build modes, and they result in the emission of the same inline function, I think it should be left up to implementations the extent to which that is supported (and again I don't think the ABI needs to talk about it). Because a contract violation cannot result in any different program behavior other than a non-returning call to a contract violation handler or UB, I think this can be thought of as "just" a derefinement problem. That is, all particular definitions of the function

void f() { function_with_contracts(); }

are refinements of

void f() {
  switch (make_nondeterminstic_choice()) {
  case 0: if (!contract) __cxa_contract_violation(...); break;
  case 1: if (!contract) unreachable;
  }
  function_with_contracts();
```

and an implementation that wishes to support mixing build modes "merely" needs to take that into account (eg, if optimization-relevant properties of the function are determined from its source-level representation).

from cxx-abi.

hamzasood avatar hamzasood commented on July 20, 2024

Yes, I think we can avoid specifying a string_view layout, either by specifying a layout or by requiring a private, constexpr contract_violation constructor with a certain signature.

As a first attempt, how about something like this?

class contract_violation {
  // Pointer/length pairs for easy string_view construction.
  const char *file_name;
  size_t file_name_len;
  const char *function_name;
  size_t function_name_len;
  const char *comment;
  size_t comment_len;

  // Placed here to minimise padding.
  uint32_t line_number;

  // Stored as an int to save space / minimise generated code size for each contract.
  // 0 -> default, 1 -> audit, 2 -> axiom.
  uint32_t assertion_level;

  // private constexpr constructor for the compiler to call.
  constexpr contract_violation(/* same as stored fields */) noexcept;
};

the translation unit that defines main provides a definition of __cxa_contract_violation

That could work quite well. I imagined it'd be a compiler flag (and unrelated to the ABI) but your option sounds better.

the violation handler will return, but the call to it was likely a tail call

I could be misinterpreting it but I think the violation handler will return regardless of continuation mode. Setting the continuation mode to off is described as "invoking std::terminate() after completing the execution of the violation handler". I interpreted that as meaning:

if (!contract) {
  __cxa_contract_violation(...); // <-- this returns
  std::terminate();
}

from cxx-abi.

zygoloid avatar zygoloid commented on July 20, 2024

We could add a call to std::terminate to each contract check, but it would be more efficient code-size-wise to instead emit:

void __cxa_contract_violation(const contract_violation&) {
  user_handler();
  std::terminate();
}

If we want to support different build modes in different TUs, then we could require two symbols instead of one (one to call the violation handler, and one to call the other and then terminate -- where the former is implicitly supplied by the TU defining main and the latter is always supplied by the ABI library). Does that seem reasonable?

from cxx-abi.

hamzasood avatar hamzasood commented on July 20, 2024

Oh, I thought that you meant the user would provide their violation handler by writing their own __cxa_violation_handler. Now I get what you mean, and yeah that sounds reasonable.

So in that case, would the library provide something like:

void __cxa_contract_violation_with_continue(const contract_violation &);

void __cxa_contract_violation(const contract_violation &cv) {
  __cxa_contract_violation_with_continue(cv);
  std::terminate();
}

from cxx-abi.

jicama avatar jicama commented on July 20, 2024

The above discussion doesn't seem to touch on how the user can provide their own violation handler.

https://github.com/arcosuc3m/clang-contracts provides a command-line compiler flag to specify what function to call, which seems like a problematic choice to me; that would lead to different TUs each using their own violation handler, which seems contrary to the EWG design.

The approach of https://github.com/lock3/gcc/tree/contracts is to make the violation handler a replaceable function like the global operator new/delete, which seems to me a better approach, though it shouldn't be called ::handle_contract_violation.

So let's say the library provides a weak definition of extern "C" __cxa_contract_violation, which users can override by defining it themselves, and that's what the compiler calls when a contract check fails.

The arcosuc3m branch defines the call-handler-then-terminate function as an internal function in each TU that needs it, which seems like a reasonable approach. I really don't want to add something to the ABI of main.

from cxx-abi.

jicama avatar jicama commented on July 20, 2024

As for string_view, it seems like a major library design problem that contract_violation in the language-support library would depend on string_view, which brings in most of the rest of the standard library. It's probably possible to finesse this so that only custom violation handlers need to depend on string_view, but IMO contract_violation should just use const char *, like type_info and exception.

For building a contract_violation, either we specify the layout and build objects up directly (as arcosuc3m) or we specify an ABI function that builds a contract_violation and passes it to the handler (as lock3). A private constructor wouldn't work, as uses of contracts don't require #include <contract>. The acosuc3m approach seems better to me, so the contract_violation object as a whole can go into .rodata.

And we need to specify the layout anyway for ABI compatibility between runtimes.

The proposed layout above includes both pointer and length for all the fields, but I'm inclined to just use a pointer for compactness; I don't think we need to support embedded NUL in these strings. That's also the choice of the arcosuc3m branch.

So,

struct contract_violation
{
  const char *file;
  const char *function;
  const char *comment;
  uint_least32_t line; // arcosuc3m and lock3 both use plain int??
  // maybe more, depending on SG21 decisions
};

Future standardization might add more data to the class, but if the default violation handler only looks at these members, that should be ABI forward-compatible.

from cxx-abi.

jwakely avatar jwakely commented on July 20, 2024

That could be:

struct contract_violation : source_location
{
  const char* comment;
};

This adds another uint_least32_t for the column number, but that shouldn't be a big deal.

from cxx-abi.

jicama avatar jicama commented on July 20, 2024

As for where preconditions are checked, both check them in the primary entry point to the function, which seems right to me. The arcosuc3m branch splits out the un-prechecked function, and mangles it with a U9unchecked type qualifier. The lock3 branch does not provide a separate entry point.

from cxx-abi.

daveedvdv avatar daveedvdv commented on July 20, 2024

I'm curious what caused this thread to be revived? (Given that contracts aren't part of the language nor do they seem likely to be part of C++23 at this point.)

from cxx-abi.

jicama avatar jicama commented on July 20, 2024

I'm curious what caused this thread to be revived? (Given that contracts aren't part of the language nor do they seem likely to be part of C++23 at this point.)

The lock3 branch was submitted for merging to GCC trunk, reviewing it made me think more about these issues.

SG21 does seem to be aiming to get agreement on a "minimum viable product" that could make C++23.

from cxx-abi.

fweimer-rh avatar fweimer-rh commented on July 20, 2024

Should the contract violation handler be configurable on a per-thread basis?

from cxx-abi.

jicama avatar jicama commented on July 20, 2024

Should the contract violation handler be configurable on a per-thread basis?

P0542 said "There should be no programmatic way of setting or modifying the violation handler", so I would think not.

from cxx-abi.

fweimer-rh avatar fweimer-rh commented on July 20, 2024

sigh This will just lead to libraries trying to provide competing handlers with their setters/getters.

from cxx-abi.

jicama avatar jicama commented on July 20, 2024

Hmm? There should be no setters/getters.

from cxx-abi.

daveedvdv avatar daveedvdv commented on July 20, 2024

I'm curious what caused this thread to be revived? (Given that contracts aren't part of the language nor do they seem likely to be part of C++23 at this point.)

The lock3 branch was submitted for merging to GCC trunk, reviewing it made me think more about these issues.

SG21 does seem to be aiming to get agreement on a "minimum viable product" that could make C++23.

Right. The pandemic has made several of the bigger items originally aimed at C++23 no-goes (and even before that some suggested that "contracts" were unlikely to recover that quickly from the C++20 fiasco).

So this is not actually discussing Itanium ABI changes at this time, right?

from cxx-abi.

rjmccall avatar rjmccall commented on July 20, 2024

We can expect that code adopting contracts will use them heavily, so we should think proactively about minimizing the costs associated with them. Additionally, if we anticipate future extension, then we should plan for that in the ABI. Therefore I have a few suggestions for contract_violation:

  • It should have a 32-bit flags field which we can use to indicate the presence of additional fields following the base.
  • One of the flags will indicate the presence of an additional field for more flags.
  • It's uncommon to see source files with more than ten thousand lines; we should support larger files, but it's okay to punish them somewhat. I would suggest that 15 bits of the flags should be used to store the line number, and a 16th bit indicates the presence of an additional 32-bit line number field:
    lineNumber = (v->hasExtendedLineNumber ? (uint64_t(v->lineNumber) << 32) + v->extendedLineNumber : v->lineNumber);
    
    I would feel slightly uncomfortable capping the range at 4 billion lines, but 128 trillion seems quite acceptable, and 32 thousand is a perfectly reasonable limit for the compressed case.
  • If the comment field is not expected to be commonly filled in, we should make it one of these additional fields.
  • I agree with @jicama that we should rely on null termination instead of including a length.
  • The structure should use relative references instead of pointers so that it can be more compact on 64-bit platforms and doesn't require eager work at load time to relocate when using position-independent code.

There is precedent for relative references in the exceptions ABI. Note that they don't strictly preclude synthesizing one of these at runtime, it's just more annoying because space for the string data may need to be copied into the structure. They do make testing significantly more annoying, though.

from cxx-abi.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.