Proposal: Method Contracts #119

Open
stephentoub opened this Issue Jan 28, 2015 · 249 comments

Projects

None yet
@stephentoub
Member

(Note: this proposal was briefly discussed in #98, the C# design notes for Jan 21, 2015. It has not been updated based on the discussion that's already occurred on that thread.)

Background

"Code Contracts" were introduced in .NET 4 as a feature of the Framework, primarily exposed through the System.Diagnostics.Contracts.Contract class. Through method calls like Contract.Requires and Contract.Ensures, developers could add code to their methods to specify preconditions and postconditions, and then a subsequent tool known as a rewriter could be used to post-process the code generated by the compiler to appropriately implement the expressed contracts, e.g. whereas the developer puts a call to Ensures at the beginning of the method, the rewriter ensures that path through the method that could cause it to exit ends with a validation of the expressed postcondition.

public int Insert(T item, int index)
{
    Contract.Requires(index >= 0 && index <= Count);
    Contract.Ensures(Contract.Result<int>() >= 0 && Contract.Result<int>() < Count);
    return InsertCore(item, index);
}

Problem

Very little code actually uses these API-based code contracts. They require additional tooling to be integrated into the build, they're verbose, and it's difficult to write additional robust tooling related to them (e.g. integrating contract information into documentation).

Solution: Language support for method contracts

Basic contract support for preconditions and postconditions can be built directly into the language, without requiring any separate tooling or custom build processes, and more thoroughly integrated throughout the language.

The compiler would generate all of the relevant code to correctly validate the contracts, and the contracts would show up as part of the method signature in the Visual Studio IDE, such as in IntelliSense at call sites to the method.

public int Insert(T item, int index)
    requires index >= 0 && index <= Count
    ensures return >= 0 && return < Count
{
    return InsertCore(item, index);
}

These contracts would primarily be validated at run time, however if the compiler (or a 3rd-party diagnostic) could statically detect a violated contract (e.g. a precondition requires that an argument be non-null, and null is being passed as the argument), it could choose to fail the compilation rather than allowing the bug to make its way to run time. Similarly, if the compiler (frontend or backend) is able to statically determine that a contract will always be satisfied, it can optimize based on that knowledge.

Unlike .NET Code Contracts, which have configurable but complicated behavior provided by a post-compilation rewriter tool (and which are no-ops if such a rewriter isn’t used), failed validation of ‘requires’ and ‘ensures’ clauses would ideally result in "fail fast" (ala Environment.FailFast(...)), meaning the program abandons immediately. This is very useful for validating preconditions and postconditions, which are typically used to catch usage errors by the developer. Such errors should never make it into production.

The compiler would require that preconditions and postconditions can be validated by the caller, and as such it requires that any members used in the requires and ensures clauses are all accessible to any caller, e.g. the requires clause of a public method may not access internal or private members, and the requires clause of an internal method may not access private members.

Preconditions and postconditions should also not throw exceptions.

Virtual methods and interface methods may have preconditions and postconditions, in which case the compiler guarantees that overrides and implementations of these methods satisfy the preconditions. To make this clear to a developer reading the code, the override or interface implementation would state "requires base" or "ensures base", to indicate that there are imposed constraints, while not forcing the developer writing the code to explicitly type them out.

interface IItemCollection
{
    int Count ensures return >= 0 { get; }
    …
}

class MyItemCollection : IItemCollection
{
    public int Count ensures base => m_count;
    …
}

Alternatives: Fail fast vs exceptions

In support of migrating existing code to use contracts, or for places where an exception is really desired over fail fast, we could optionally consider additional syntax to specify that an exception should be thrown instead of fail fasting.

For example, the previous Insert example's requires and ensures clauses would result in fail fasting if a condition isn't met:

public int Insert(T item, int index)
    requires index >= 0 && index <= Count
    ensures return >= 0 && return < Count
{
    return InsertCore(item, index);
}

But, the developer could explicitly override the fail fast by specifying what should happen in case of failure:

public int Insert(T item, int index)
    requires index >= 0 && index <= Count else throw new ArgumentOutOfRangeException(nameof(index))
    ensures return >= 0 && return < Count
{
    return InsertCore(item, index);
}
@aarondandy

Any possibility of getting invariants as well?

Would love to see the static analysis tools we have in Code Contracts working with these new contracts.

I really like the presented syntax to decide between exceptions and fail-fast for preconditions.

@aarondandy

Could some kind of flag be provided to omit private/internal contracts from release builds if we have static analysis tools to prove them?

@jaredpar
Member

@aarondandy

In previous research we have shied way from invariants in part because it's actually incredibly difficult for developers to predict the effect it will have on the code. Typically invariants checks are validated to be true on every exit / entry point of a given method. Given that can you point out all of the places on the following method where that happens?

class C
{
  invarint Count >= 0

  int[] _array;
  int _count;

  void AddRange(IEnumerable<int> all) 
  {
    EnsureCapacity();
    foreach (var cur in all) {
      _array[i] = cur;
    }
  }
}

Here are just a few of the places where an invariant check must occur in the above code:

  • In the generated call to GetEnumerator.
  • Twice per loop iteration. Once for MoveNext and Current each.
  • Before and after the call to EnsureCapacity
  • At least twice inside the implementation of EnsureCapacity
  • At the beginning and end of the method.

Sure a few of these may be able to be optimized away. But even then on this really simple method the invariant check is occurring at a very high frequency.

In the past when we looked deeply at places where developers wanted invariants we found that requires and ensures were sufficient to the task.

@aarondandy

@jaredpar

That is fair considering the complexity The real reason I want invariants is just to reduce the amount of redundant contracts I would need to write. As a compromise would it be terrible to have a way to reuse some pre/post conditions? Something like [ContractAbbreviator] but probably way different?

@theoy theoy added the Language-C# label Jan 28, 2015
@mirhagk
mirhagk commented Jan 28, 2015

@aarondandy Are you referring to reusing a pre-condition as a post-condition, or referring to reusing a precondition across several methods in a class?

For the first situation it'd be nice if requires ensures or some other syntactic sugar was supported. For the second, it could be handled with allowing the expression to be a method (which can be declared with the shorthand lambda method syntax), so you can have a CheckClassInvariants method and do requires ensures CheckClassInvariants() which isn't too bad.

@aarondandy

@mirhagk

"reusing a precondition across several methods" is what I meant but I like your other thoughts too. I don't really want to have a true provable invariant for Count >=0 but I definitely want a way to prove and communicate that each method call on my type won't do something crazy to Count.

@ejball
ejball commented Jan 28, 2015

I wonder if ensures could be made to work naturally with async method results.

@apskim
apskim commented Jan 28, 2015

Good to see that this is being considered!

Preconditions and postconditions should also not throw exceptions.

How are you going to verify this? For instance, are we allowed to do property access in contracts: requires id != this.Owner.Id? What if Owner is null? Is it an uncompilable code, a NullReferenceException at runtime, or a contract failure?

Just like in API-based Code Contracts, there should be some way to refer to the old (pre-invocation) value of the field/property/parameter. old(foo) looks good, but that makes old a new contextual keyword, which you don't like to introduce often.

@mirhagk
mirhagk commented Jan 28, 2015

@apskim

Preconditions and postconditions should also not throw exceptions.

This may or may not be enforced. It might just be a "you shouldn't do that" kind of thing rather than a compile time exception (perhaps static analyzers could look for it).

If it is a compile time thing, then regular property access would have to be disallowed in your case to instead do something like requires id <= this.Owner.?Id. This doesn't throw any exceptions and will fail the validation.

@drewnoakes
Contributor

To what extent can the compiler check a type's contracts for internal consistency? For example, it'd be hard to reliably solve for arbitrary expressions:

class C
{
    double Value ensures Math.log10(return) > 0 { get; }

    void Foo() ensures Value < 1 { ... }
}

There is no value of x < 1 for which Math.log10(x) > 0.

Would these constraints primarily be evaluated at run-time, with best-effort use from tooling?


Also, if arbitrary code can be executed in requires and ensures statements, side effects must be considered. Code may behave differently when compiled to evaluate contracts vs. without.

@mirhagk
mirhagk commented Jan 28, 2015

@drewnoakes I'm assuming that an extension will be created that checks all sorts of crazy situations that you'd never want to add to a compiler (for complexity reasons). The compiler can do very minimum checks, and leave most of the checks to extensions.

In #98 it was mentioned that there may want to be some shorthand syntax for common checks (null being the most common). Something like requires item seems like a simple shorthand for the case of null checks, and combined with the ability to separate with a , would probably lead to a wider usage (requires item, data, index would be very simple to write and there'd be little reason to not use it).

An alternative is to use requires item! which makes use of the proposed non-nullable reference syntax ! and separates it from a boolean.

@Clockwork-Muse

what about situations where a compile-time check is nice, but a runtime check may impart more of a performance penalty? For example, there's this comment in System.Collections.Immutable's array:

public T this[int index]
{
    get
    {
        // We intentionally do not check this.array != null, and throw NullReferenceException
        // if this is called while uninitialized.
        // The reason for this is perf.
        // Length and the indexer must be absolutely trivially implemented for the JIT optimization
        // of removing array bounds checking to work.
        return this.array[index];
    }
}

...which states that, while you want compile-time checks for existence of the array, and index range, the penalties for runtime checks are too high. (In this case they're letting the hardware/vm throw the errors directly, a rarer occurrence than checking all accesses)

Maybe the validator could biggyback off of hardware/vm protections? So it could see something like requires this.array, index >= 0 && index < this.array.Length, note that it has built-in runtime checks (effectively) for those values anyways, and noop them. Which would probably require sitting in the compiler, than as an external tool.

@jaredpar
Member

@Clockwork-Muse

We've did pretty extensive profiling of contracts in our project and found that in general the performance hit they caused was minimal. It was usually in the 1-2% range across the system.

Sure there was the occasional check that was too expensive to make a contract. In those situations we first looked to the API itself. If verifying the invariants of the type was too expensive to put in a contract then perhaps the API itself needs to be changed.

If the API couldn't be made more efficient then we would move the check into Debug.Assert. That was pretty rare though (only a handful of occurrences).

Note: contracts do not always result in a negative performance hit. It is possible that they establish invariants (like bounds) that the JIT can optimize against and result in faster code. There were definitely places where that happened.

@AdamSpeight2008
Contributor

Would it be possible to have a method contract in this position?

T Item(int index)
{
  requires (0 <= index) && (index < Size)
  get{ return _a[index]; }
  set(T item) { _a[index]=item; }
}

Using a trait ( #129)

T Item(int index)
{
  trait { _a!; range:  0 <= index < _a,Length; }
  get { _a[index]; }
  set(T item) { _a[index] = item; }
}
@brandf
brandf commented Jan 31, 2015

The part about Virtual methods and interface methods made me starting thinking about how this might be seen as an extension to the type system in a way...or rather another kind of type, similar to units of measure in F#. I'm bored, so I'm going to riff on the idea...

If we start with the first example in the solution above:

public int Insert(T item, int index)
    requires index >= 0 && index <= Count
    ensures return >= 0 && return < Count
{
    return InsertCore(item, index);
}

The 'requires' are additional constraints on the methods parameters, similar to how you have a types associated with each parameter. For fun I'm using 'where' below instead of adding new keywords. I think it's worth considering making it look LINQ'ish:

public int Insert(T item, int index where index >= 0 && index <= Count)
    where return >= 0 && return < Count
{
    return InsertCore(item, index);
}

Which then makes me think of the notion of generic type constraints. Similar to how a delegate gives a name to the shape of a function, a typeconstraint is used to give a name to a constraint.

typeconstraint Bounded<Type, Lower, Upper> = Type N where N >=0 Lower && N <= Upper

Perhaps you could then use typeconstraints anywhere you would normally use a type. The generic parameters could be bound to other types, constants, members or other arguments. Now the above can be rewritten:

public Bounded<int, 0, Count> Insert(T item, Bounded<int, 0, Count> index) 
{
    return InsertCore(item, index);
}

...which is looking pretty clean IMO.

And maybe I'm going off the deep end here, but lets consider going a step further by thinking of the traditional type syntax as being syntactic sugar for a specific where clause...

typeconstraint Bounded<T, Lower, Upper> n where typeof(n) is typeof(T) && n >= Lower && n <= Upper

In other words, 'traditional type syntax'...

int x

...is shorthand for...

var x where typeof(x) == typeof(int)

...or for parameters and returns...

x where typeof(x) == typeof(int)

Since non-nullable types are being considered at C#7 design meetings, lets see how that fits in with this:

typeconstraint NotNull n where n != null

...and union types (a bit like TypeScript has)

typeconstraint Union<T1, T2> n where typeof(n) == typeof(T1) || typeof(n) == typeof(T2)

And of course, similar to delegates, an assembly should be able to expose public typeconstraints like any other kind of type.

@gafter gafter added the 1 - Planning label Feb 2, 2015
@MadsTorgersen MadsTorgersen was assigned by gafter Feb 2, 2015
@chrisaut
chrisaut commented Feb 4, 2015

I mentioned it in the original discussion, but what about a short hand syntax for the most common scenarios:

public void M(string! x) {}

expands to:

public void M(string x) requies x != null {}
public void M(int >= 0 x) {}

expands to:

public void M(int x) requires x >= 0 {}
public int >= 0 Count() {}

expands to:

public int Count() ensures return >= 0 {}
public void M(int < y x, int > x y) {}

expands to:

public void M(int x, int y) requires x < y, requires y > x {}

(this specific case looks messy, thus probably ok to only support short hand for simple cases, like operators and constant literals (no method calls, no refering to other arguments or fields/properties etc, for that you would use the "full" syntax).

public C! M(B! b) {}

expands to:

public C M(B b) requires m != null, ensures return != null

I think of contracts as a harder "type", thus I feel it might make sense to have the requirements right there with the parameter (it also avoids double typing).
For instance you don't say
public M(x) requires x is int, the int requirement is right there with the parameter, so why would further requirements on x not be right along there with it?

The downside is it might look a bit messy, especially if there are lots of parameters (but that's code smell anyways), and I guess it's (much?) harder to parse for the compiler.

@AdamSpeight2008
Contributor

@chrisaut public void M(int < y x, int > x y) {} can never be satisfied

I think the ( #129 ) would be a better approach.

public void M(int x, int y)
  trait { x < y; y > x }
  {}

As it doesn't mess with the parameters syntax, yet still available for the IDE to use the user and collapse the trait definition.

@chrisaut
chrisaut commented Feb 4, 2015
@chrisaut public void M(int < y x, int > x y) {} can never be satisfied

why not?
it expands to:

public void M(int x, int y) requires x < y, requires y > x {}

x needs to be smaller than y, and y needs to be bigger than x (which comes from the first constraint, you wouldn't even need the second constraint).
M(0, 1) satisfies it.
But it just goes to show that for anything but trivial "< constant" stuff the syntax is confusing, hence it's probably not a good idea to allow it for anything that referes to other named parameters/fields/etc.

@AdamSpeight2008
Contributor

May have misread it as x < y and y < x

@sirinath
sirinath commented Feb 5, 2015

I don't see why this should be method only. Why not be able to use with variables also. In addition some distinction on runtime, compile time if unable at compile time at runtime and enforceability.

@vkhorikov

@jaredpar consider binding this feature to non-nullable reference types.
Although they can't be implemented as they are implemented in, for example, F# but some kind of 'global precondition' can mitigate the problem: it can state that no reference type within a class can have null value.
This will help to reduce amount of '!= null' preconditions in code that tries to be more functional.

@johncrim
johncrim commented Feb 9, 2015

Could you explain why run-time contract violation shouldn't throw an exception? I've shied away from using CodeContracts static analysis (build are too slow; too much work to fix all warnings), but find them great for run-time validation and documentation. It's also great to be able to dial up and down the amount of run-time checks for debug and release builds (full checks vs ReleaseRequires). In the case of release builds, you DEFINITELY don't want fail-fast behavior; and arguably you'd want consistent behavior but more checks in debug builds.

My biggest problems with CodeContracts have been:

  1. The post-processing step slows down the build a HUGE amount (CodeContracts seems to account for 80% of the build time). This is the main reason I've considered dropping CodeContracts on several occasions.
  2. The syntax could be more readable/expressive/concise
  3. The implementation is completely broken on Mono - this is the primary reason why developers with Macs are having to run Windows VMs.

I would expect that this proposal would address 1 and 2 at a minimum, so I'm a huge fan.

Another important trait to preserve is the ability to intercept contract failures and customize the response: log them, pop up a dialog, etc.

@jaredpar
Member
jaredpar commented Feb 9, 2015

@johncrim

Could you explain why run-time contract violation shouldn't throw an exception?

Contract violations occur when code has called into a method and provided invalid arguments or when the type itself was in an invalid state. This represents a coding error in the calling code. It simply failed to meet the basic contract of the method in question.

The safest thing to do in the face of a coding error is to stop executing code. The program is in an undefined state and continuing to execute code could lead to all manner of issues including: data corruption, later errors which mask the original bug, etc ...

This is why contract violations use Environment.FailFast (or the like) in the event of a failure. It ensures the process will be torn down in the most expedient manner possible. Crash dumps will point directly at the bug in question. Throwing an exception is instead a way to guarantee that user code will continue executing the program in an undefined state.

@ulrichb
ulrichb commented Feb 9, 2015

I totally agree that in theory a developer's / coding error in the calling code should immediately stop the program.

But: Are we sure that in practice there are no legitimate use cases where we want to fail into a safe state in an exception handler (i.e. log errors using existing infrastructure; send error replies in a distributed system; ...) even in case of developer errors i.e. contract violations at run-time?

@jaredpar
Member

@ulrichb

Sure but in those case I would say that exceptions are a better choice than contracts. In my mind I view the difference between exceptions and contracts as follows:

  • Contracts: a precondition than can easily and efficiently be tested / guaranteed by the caller. If this precondition is violated then it clearly indicates a bug in the program.
  • Exceptions: used when a situation where a precondition can't efficiently, conveniently or simply can't be tested / guaranteed by the caller. Greatest example here is IO (it can always fail).
@ulrichb
ulrichb commented Feb 10, 2015

@jaredpar

What I meant was, that even in the case that we have a contract violation due to a developer error / bug in the program, do we always want to FailFast()?

One example: If we implement an HTTP server and request input leads to a precondition violation (due to a bug in the program), do we want to crash the whole server application (including all other request threads)? I think we should have the possibility that the contract violation throws an exception, i.e. just aborts the current request, and logs the contract violation, like it would happen with thrown NullReferenceExceptions (which are also bugs).

@aarondandy

@jaredpar

I could understand that view if static analysis tools were forced to run for everybody. Currently with the Code Contracts implementation I think it is rare for a user of a library to actually run static analysis against their project. Even if it was baked into the compiler I would think that especially during day-to-day development activities some people would want to temporarily disable static analysis if they are building and testing frequently. That is how I work anyway. In these cases it is pretty nice if I just get an ArgumentNull exception that I can see in my test and say "oops!"

I do still agree with "contract violation shouldn't throw an exception" but for a totally different reason I think. I like to see it kind of like driving with a sharp spike on your steering wheel, you would hopefully take extra care to not rear-end anybody. I find that when I am more likely to get an exception (or any failure really) due to violating a contract I am less likely to violate the contract as it makes me more careful. Static analysis tools help a bunch as well in that area.

@jaredpar
Member

@ulrichb

One example: If we implement an HTTP server and request input leads to a precondition violation (due to a bug in the program), do we want to crash the whole server application (including all other request threads)?

This is confusing user input validation with method argument validation.

User input should never be done with a contract because as you pointed out it shouldn't be a tool to crash the process. That type of sanitation should be a separate process.

@jaredpar
Member

@aarondandy

I think it is rare for a user of a library to actually run static analysis against their project. Even if it was baked into the compiler I would think that especially during day-to-day development activities some people would want to temporarily disable static analysis if they are building and testing frequently

I do want to be clear that the compiler won't be doing any static analysis on the contracts the users supplies. These are simply a tool for argument validation.

That being said the output of contracts is expressive enough that theorem provers can run over the code.

@ulrichb
ulrichb commented Feb 10, 2015

@jaredpar

... leads to a precondition violation (due to a bug in the program) ...

@jaredpar
Member

@ulrichb

How is that inconsistent with my argument?

@aarondandy

@jaredpar I may have misunderstood your previous comments, so ignore if so. The point I am making is that if FastFail is the only way that contracts are enforced I think it would be vary inconvenient for users of those methods. I think that having a choice between a contract throwing an exception or doing fastfail is important.

@ulrichb
ulrichb commented Feb 10, 2015

How is that inconsistent with my argument?
User input should never be done with a contract because as you pointed out it shouldn't be a tool to crash the process. That type of sanitation should be a separate process.

You again ignored my assumption. Given we have a bug in our program and user input is passed non-sanitized to a precondition-guarded method. Given this assumption, do we always want to FailFast?

@jaredpar
Member

@ulrichb

Given we have a bug in our program and user input is passed non-sanitized to a precondition-guarded method. Given this assumption, do we always want to FailFast?

Yes.

The alternative is to continue running a program which has just proven that it's not doing user input sanitation. Allowing it to continue is a recipe for attacks like SQL injections to occur.

@aarondandy

@ulrichb @jaredpar

But I could also terminate execution by not catching the exception or (properly) re-throwing it. You may not agree with somebody's view on how to handle contract failures like they don't agree with yours. I think that right there shows that a choice should be allowed.

@ulrichb
ulrichb commented Feb 10, 2015

@jaredpar

Allowing it to continue is a recipe for attacks like SQL injections to occur.

I never spoke about continuing processing the request. My example was to enter a safe state:

... aborts the current request, and logs the contract violation ...

@jaredpar
Member

@aarondandy

I think that right there shows that a choice should be allowed.

Developers do have a choice: if believe that failure of a particular precondition represents a recoverable action then don't use contracts, use manual verification and exceptions as they are today. Contracts should only be used for places that indicate programming errors.

@RichiCoder1

@jaredpar aka. the eponymous // this should never happen?

@fschmied

@jaredpar As you said, contracts are simply a tool for argument validation. Passing arguments that do not fulfill the contract is a programming error.

Programming errors exist in code during development and QA. If code currently being developed or quality-tested crashed the current process (assuming Environment.FailFast), development, testing, and bug analysis would all become very inconvenient. (Think about the unit test runner crashing all the time because of programming errors. No more red tests, just abandoned execution.)

Programming errors exist even in production code, if they weren't found during development and QA. If production code failed fast due to a bug without any chance to produce a log, production bug analysis would become even harder than it already is. If a whole web server crashed due to one request triggering a bug, a small error could suddenly become a tremendous security issue - denial of service made easy.

The .NET Framework already contains a number of exception classes used to report contract validations; see the family of ArgumentExceptions. Similarly, deferencing a null value or performing an invalid cast, which clearly are programming errors, throw NullReferenceExceptions and InvalidOperationExceptions. The same reasoning of why these exceptions exist also applies to a new hypothetic language-supported contract feature.

Exceptions indicating that a programming error occurred are not meant to be caught and handled (or even ignored). They are not provided as a way to continue execution in a broken state. They are instead meant as a means to terminate execution of the current "operation", falling back the call stack up to a safe location, where the application can decide what to do about that unexpected error. It can log it, decide to terminate the current request, etc.

@jaredpar
Member

@fschmied

They are not provided as a way to continue execution in a broken state. They are instead meant as a means to terminate execution of the current "operation", falling back the call stack up to a safe location, where the application can decide what to do about that unexpected error.

This is the root of the disagreement. How can the application make an educated decision that it is a safe location? The application has identified that the code it is calling has a coding error in it. That is all it knows.

It has no understanding of why this failure occurred, if the code was left in a recoverable state or really even what component the failure came from. The error could have occurred directly in the code that was called or even transitive dependencies. The argument exception could be anything from a negative index to passing unsatized user input to a back end. There is just no way of knowing.

The application simply cannot make an informed decision on whether or not it is safe to continue executing. This is why termination is the best option.

@mirhagk
mirhagk commented Feb 13, 2015

The application simply cannot make an informed decision on whether or not it is safe to continue executing. This is why termination is the best option.

There are quite a few circumstances when the application can make an informed decision to continue executing. If action is isolated and not critical to the application itself, then it can carry on. If you have a feature in your game to share your high score with your friends, it doesn't need to kill the entire application when saving the high score fails a contract.

Your POV seems to be that any programming error could leave the program in a completely undefined state, but this isn't like C++ where the stack could be corrupted by any section of the code. You can know which parts of the program can't be affected and are safe to continue. There may be situations where you are writing critical code and maybe you do want fail fast, but I hardly think that it's impossible to know which part of your program failed.

It's just simply ridiculous to assume that the correct action would always be to kill the process immediately. If microsoft word had a bug when it saved to a network path because of a coding error, would you want it to immediately kill the app? No, you'd want it to save the file to a temporary location, give an error message to the user, log the problem, and then offer to try and restore the file when it loads next time.

@HaloFour

I think that it's absolutely silly for a contract violation on argument validation to cause the entire process to crash. Implementing it that way is a sure-fire way to guarantee that the feature is never used, at least by anyone writing anything non-esoteric. The point of that side of this feature is argument validation, and argument validation is inherently something from which the program can recover in some manner. And frankly, if it can't, the caller can make that decision by not catching the exception. This is how all implementations of code contracts that I've ever seen works on .NET now or in any other language.

On the other side of contracts, output validation, I can somewhat more understand this decision. In that case the state really is corrupt and it's hard to determine what is the appropriate course of action. However, even then I think failing fast is horribly abrupt.

In short, to fail fast is not only incredibly unintuitive and unexpected, but it will also wreak absolute havoc on all existing testing frameworks designed to help developers find and catch these very problems. Rather than make developer's lives easier it will only make it significantly harder. Or, frankly, it just won't be used, at least by anybody who understands what it does.

@jaredpar
Member

@mirhagk

Your POV seems to be that any programming error could leave the program in a completely undefined state, but this isn't like C++ where the stack could be corrupted by any section of the code.

True, managed code makes it less likely, though definitely not impossible, for memory / stack to be in corrupted state. However this is not the only type of bug that can lead to undefined behavior. There are many other cases:

  • Aborting a write to a file buffer in memory half way through leaving the file in a corrupted state.
  • Failing to properly secure a https channel.
  • Failing to properly sanitize inputs.

These are represent real errors that can lead to data loss or data exposure.

Customers will be annoyed if your program crashes a lot, they'll be furious if you corrupt their data.

You can know which parts of the program can't be affected and are safe to continue.

Sorry this is just not true. It is nearly impossible in a sufficiently large program to isolate the effects on data from one piece of code to another. There are just so many ambient ways in which they can share data.

If microsoft word had a bug when it saved to a network path because of a coding error, would you want it to immediately kill the app?

This is an example of IO, an operation which is fundamentally unpredictable and not suitable for contracts. This is instead a place where exceptions shine.

@jaredpar
Member

@HaloFour

I think that it's absolutely silly for a contract violation on argument validation to cause the entire process to crash. Implementing it that way is a sure-fire way to guarantee that the feature is never used, at least by anyone writing anything non-esoteric.

I don't take the position that contract violations should cause a process failure lightly. I have this belief after years of repeated experiences across a wide variety of code bases where we moved from Debug.Assert or exception style validation to fail fast semantics. Every single time it had the same results.

Short term instability did increase. Years of bugs that were hidden by debug only semantics or exceptions that were eventually swallowed were suddenly un-ignorable (a process crash is a sure fire way of getting a bug filed). After about a month though two things began to happen.

  1. Product stability dramatically increased.
  2. Lots of bugs simply stopped reproing. Turns out they were just side effects of bugs the weak argument validation was hiding.

This pattern repeated across a variety of code bases I worked on over the years. These code bases ranged from small (about ten thousand lines) to very large (millions of lines).

Rather than make developer's lives easier it will only make it significantly harder. Or, frankly, it just won't be used, at least by anybody who understands what it does.

This is exactly how I felt the first time I was introduced to this concept. I thought it was a mistake and that we'd suffer greatly from it. After a couple weeks of living with it and seeing the bugs it was finding I became a convert. All my experiences since then have re-enforced this stance.

All my arguments being made I think this feature is more likely to end up with the two varieties:

  • Fail fast: requires <condition>
  • Fail with exceptions: requires <condition> else throw <exception>

The reason being that it's a great back compat story. It allows the unification of all of the existing forms of argument validation into one single feature with a more concise and documentable syntax.

Even if this is the eventual outcome I highly encourage you to embrace fail fast. Yes it will cause short term instability in the code base. In the long run though it will make your code better by removing a whole class of silent failures and the strange bugs that result from them.

@aarondandy

@jaredpar

I would totally use fail fast, but I would like to be able to use that within my private surface. I really want exceptions for my public surface. I feel that if a user of my code decides to feed exceptions to the exception monster, that is their choice and they (implicitly, their users too) will deal with the consequences.

While you may see the light and I sometimes see it too I don't think people will be very into contracts if it is 100% fail fast, it's just not friendly enough to be practical in my opinion. I will defend and make noise in favor of fail fast just as I do for exceptions however. I want them both and think they both have their place in my code. I don't consider myself special so I am certain that there are many others out there who feel as I do. I think the world would be better with exceptions than with no contracts at all and I strongly feel that people will not take kindly to it, if it is 100% fail fast.

The bigger deal to me is the static analysis. I already have if and throw and Code Contracts so I don't need code contracts added to roslyn. I want something better though, who doesn't? I know that has nothing to do with roslyn from an implementation point of view but if contracts and exceptions are to be separated, I at least want one unified way of encoding contracts for analysis, for both "roslyn contracts" and exceptions, so that they can all be processed together by tools. Xml docs are not enough.

@Przemyslaw-W

How about exception/fail fast switch in app manifest?

2015-02-13 5:20 GMT+01:00 Aaron Dandy notifications@github.com:

@jaredpar https://github.com/jaredpar

I would totally use fail fast, but I would like to be able to use that
within my private surface. I really want exceptions for my public surface.
I feel that if a user of my code decides to feed exceptions to the
exception monster, that is their choice and they (implicitly, their users
too) will deal with the consequences.

While you may see the light and I sometimes see it too I don't think
people will be very into contracts if it is 100% fail fast, it's just not
friendly enough to be practical in my opinion. I will defend and make noise
in favor of fail fast just as I do for exceptions however. I want them both
and think they both have their place in my code. I don't consider myself
special so I am certain that there are many others out there who feel as I
do. I think the world would be better with exceptions than with no
contracts at all and I strongly feel that people will not take kindly to
it, if it is 100% fail fast.

The bigger deal to me is the static analysis. I already have if and throw
and Code Contracts so I don't need code contracts added to roslyn. I want
something better though, who doesn't? I know that has nothing to do with
roslyn from an implementation point of view but if contracts and exceptions
are to be separated, I at least want one unified way of encoding contracts
for analysis, for both "roslyn contracts" and exceptions, so that they can
all be processed together by tools. Xml docs are not enough.


Reply to this email directly or view it on GitHub
#119 (comment).

@fschmied

@jaredpar

This is the root of the disagreement. How can the application make an educated decision that it is a safe location? The application has identified that the code it is calling has a coding error in it. That is all it knows.
[...]
The application simply cannot make an informed decision on whether or not it is safe to continue executing. This is why termination is the best option.

This is not about continuing executing. This is mainly about allowing application code to decide how to fail. Failing could mean:

  • logging, then terminating;
  • showing UI and sending a bug report, then terminating;
  • saving as much of the user's work as possible, then terminating;
  • immediately terminating;
  • only terminating the Application Domain;
  • only terminating the current Web Request.

You can know which parts of the program can't be affected and are safe to continue.

Sorry this is just not true. It is nearly impossible in a sufficiently large program to isolate the effects on data from one piece of code to another. There are just so many ambient ways in which they can share data.

It is safe to execute code after a programming error occurs

  • if you know that the block of code that triggered the programming error cannot have any side effects on the rest fo the application (and balance the probability of your being wrong against the risk), or
  • if you only execute code explicitly crafted to run in case of a programming error.

I (and I think the others) are not advocating generally catching the exception and just continue running as if nothing happened. But a general purpose programming language simply cannot assume that it is okay to call Environment.FailFast when a programmer made a mistake, this really depends on the concrete application.

As @mirhagk wrote:

If microsoft word had a bug when it saved to a network path because of a coding error, would you want it to immediately kill the app?

This is an example of IO, an operation which is fundamentally unpredictable and not suitable for contracts. This is instead a place where exceptions shine.

No, it can also be because of a coding error. Assume the programmers of Microsoft Word thought that a path had certain properties and built a corresponding contract check. In production, a certain network path does not fulfill those properties, and the contract check fails. You would of course not want Word to simply catch that unexpected error and pretend nothing happened. But you would also not want Word to crash immediately. Instead, you want Word to log the error, try to save your contents as well as it can to a temporary location for recovery, show a user interface, then quit.

The application developers have to be able to decide what happens when an unexpected programming error occurs.

All my arguments being made I think this feature is more likely to end up with the two varieties: [...]

That's not enough. The else throw new ... syntax is great for customizing what exception is thrown. But if the ordinary contract syntax always failed fast, you

  • couldn't use it in web applications (being able to take down a whole web server process imposes the security risk of cheap DoS attacks)
  • couldn't use it in client applications that want to be able to recover a user's work in case of a programming error,
  • couldn't use it in client applications that want to display UI before crashing,
  • couldn't use it in applications that need to implement error logging or error telemetry,
  • couldn't use it in libraries (don't know what application is using the library).

In fact, you could only use it in applications where it's okay to immediately crash when a programmer's bug is encountered.

About what you yourself wrote:

I don't take the position that contract violations should cause a process failure lightly. I have this belief after years of repeated experiences across a wide variety of code bases where we moved from Debug.Assert or exception style validation to fail fast semantics. Every single time it had the same results.

Short term instability did increase. Years of bugs that were hidden by debug only semantics or exceptions that were eventually swallowed were suddenly un-ignorable (a process crash is a sure fire way of getting a bug filed).

First, I'm not talking about using debug assertions for contract checking. I'm very much opposed to that. I'm talking about exceptions for signaling contract validation only.

Second, in "exceptions that were eventuelly swallowed", exceptions are not the problem. Swallowing exceptions that indicate programming errors is. IMO, failing fast always is like throwing out the baby with the bath water.

@Przemyslaw-W That would be one way of solving it, yes.

@HaloFour

I'd rather see method contracts throw exceptions (at least for the requires clause) and add a new language keyword assert to fail-fast if whatever condition is not met.

If you polled regarding this feature I'm sure that the vast, vast majority of developers just want a simpler way to validate arguments and to throw some variation of ArgumentException in the case that the argument is invalid. The alternate syntax for method contracts makes this possible (assuming it would be adopted) but manages to actually be more verbose than coding it by hand. As such a developer has very little impetus to use method contracts.

I agree that Debug.Assert is basically useless and I wouldn't associate method contracts with it at all.

As for the concern about developers just swallowing validation exceptions, sure, that is a valid concern. But the developer has to opt-into being stupid and the language shouldn't be second-guessing whether or not the developer had a valid reason for wanting to handle the exception differently. Given that the default behavior of the run time if an exception manages to bubble up to the threadproc is to fail fast anyway I don't see why method contracts should be jumping the gun on that decision.

@MgSam
MgSam commented Feb 13, 2015

I think "failing fast" is a horrible idea for all the reasons mentioned by other posts. It takes all control away from the programmer. If that was the default behavior of method contracts I can guarantee that I and many others would never use them.

The runtime and language has a robust exception mechanism that allows the user to decide how to handle errors. It's not the role of the language designers to make this decision for us.

Throwing exceptions should absolutely be the default behavior of any method contract.

@RichiCoder1

I'm going to throw in my two cents here and agree with @jaredpar, with caveats. I agree that there's a difference between input validation and sanitation, and code contracts. Things like IO often can have unexpected and undefined circumstances, so exceptions fit well there. But if I'm in a core library and I receive unexpected input, then something's seriously wrong and I want to bail out (fail fast).

That being said, I also see and understand the concerns others are voicing. The biggest contention seems to be that most everyone else wants a shorthand for validating method parameters rather than strongly enforced contracts. Not only that, I do agree with the issue of not being able to record what contracts failed in the application.

Some possible solution (some mentioned above) could be:

  1. Providing two different contract keywords. One (requires) which produces an exception and another (asserts) that triggers a FailFast.
  2. A reliable extension point where developers could record failures that trigger Enviroment.FailFast.

How about exception/fail fast switch in app manifest?

This seems far to non-deterministic to me.

@jaredpar
Member

@fschmied

It is safe to execute code after a programming error occurs

The bar you laid out here is very high and sure in those very specific circumstances it would be okay. The number of places in real code though which meet that high bar are very few.

But a general purpose programming language simply cannot assume that it is okay to call Environment.FailFast when a programmer made a mistake, this really depends on the concrete application.

As some whose implemented this feature in a programming language used across a wide variety of application scenarios I very much disagree.

@HaloFour

@RichiCoder1 Yes, I think that's missing is the capacity to succinctly express the severity of if the condition fails. I think something like assert (not to be confused with Debug.Assert) captures this pretty well, either in the contract portion or as a standalone keyword.

However, even then I'm a little hesitant about failing fast only because doing so is so unfriendly to other tooling like test frameworks. I'd probably like to see a way to control that at the application config level so that test runners can be updated/configured to not simply explode on a failed assertion. This wouldn't be dissimilar to the application config key which would allow a process to continue executing if an unhandled exception bubbled up past the threadproc as that behavior was changed in .NET 2.0.

@HaloFour

@jaredpar

The bar you laid out here is very high and sure in those very specific circumstances it would be okay. The number of places in real code though which meet that high bar are very few.

We're going to have to disagree. My experience is very much the opposite, at least when it comes to contracts regarding argument validation. I'm sure that you can enlighten me about any other languages or code contract frameworks that behave in this fashion? I've yet to see one.

Even Eiffel just throws an exception.

@jaredpar
Member

@HaloFour

I'm sure that you can enlighten me about any other languages or code contract frameworks that behave in this fashion? I've yet to see one.

Most of the framework mechanisms I've seen / used has simply been building off of existing ones or simply hand implementing them:

  • Switching C++ validation macros from asserts to hard fail logic.
  • Hooking into events like Contract.ContractFailed to crash the process vs. letting normal exception semantics occur.
  • Hand implemented APIs like Contract.Requires plugging into Environment.FailFast
  • etc ...

Language wise there aren't any main stream languages that I'm aware of which have a hard contract system. Most shy away from argument validation entirely. There are a few out there with contracts but they tend to be on the theorem proving side which is not what this feature is about.

However part of my previous job was adding contracts, and other extensions, to C#. The resulting language was applied across a wide variety of applications and use cases in a very large code base.

Contracts was particularly exciting to add because up until then I'd only seen it deployed as ad-hoc frameworks. I was curious to see how it would scale to an actual language implementation and it did not disappoint. The language integration allowed us to take it a bit further than a run of the mill API would have (plugging into inheritance and interfaces in particular).

The research team viewed contracts as a very successful language feature for many of the reasons I've previously listed. This particular proposal stems directly from that implementation.

We're going to have to disagree.

That's a fine outcome. I understand this can be seen as an intimidating and possibly reckless approach to argument validation. I assure you though it is not :)

I still encourage you, and others, to try it. I've seen it implemented a number of times now, almost always in the face of opposition, and every time it's been viewed as a success.

@RichiCoder1

@jaredpar How have the products you've used this with captured diagnostic information about failures so this issues could be fixed? Did you just lean on Window's error reporting/logging? How was testing handled?

@aarondandy

@HaloFour

I like the assert idea but I think the syntax require expression else fail would be more in line with the others and more explicit about what will happen. require expression could be used to assert and require expression else throw Exception() for exceptions. Assert and Fast fail could also be toggled using a switch too, as previously suggested. Just some thoughts.

@jaredpar
Member

@RichiCoder1

Yes we just relied on Windows error reporting / watson for post mortem debugging.

@jaredpar
Member

@Przemyslaw-W

I'm very much against a switch to toggle fail fast on / off. Having the behavior of the application changed based on a togglable setting makes code that much harder to read. For every call site to a method it implicitly doubles the number of possible code paths possible. It also double the test matrix of the code in question.

@HaloFour

@jaredpar If this system was similarly pluggable and the developer could make the decision then I wouldn't be as opposed to it. The default behavior being fail-fast simply does not appeal to me. At the very least support an app.config method to specifying the behavior. Having a test runner just die is ludicrous.

As for trying it, I primarily write libraries consumed by other developers. If using my library caused their process to fail the end result will simply be that they will stop using my library. I could point them to crash dumps all day long, most developers have no idea what to do with those things anyway. They understand stack traces and failing tests.

I can guarantee you that if fail-fast is the default behavior that this feature will go unused, you're better off spinning that effort elsewhere.

@jaredpar
Member

@HaloFour

I can guarantee you that if fail-fast is the default behavior that this feature will go unused, you're better off spinning that effort elsewhere.

This is a point we will have to disagree. I've implemented this feature in libraries and in languages. It was not shunned by any means but very much embraced.

@HaloFour

@jaredpar

This is a point we will have to disagree. I've implemented this feature in libraries and in languages. It was not shunned by any means but very much embraced.

The community seems to think otherwise and I've yet to see compelling arguments for the "fail-fast"
case beyond "trust me".

I write unit tests to ensure that my methods fail when I pass them invalid arguments. This makes it impossible to do that. There goes the test runner again.

@jaredpar
Member

@HaloFour

The community seems to think otherwise and I've yet to see compelling arguments for the "fail-fast"
case beyond "trust me".

The community is far more than the 10 or so people who contribute to a GitHub discussion.

@mirhagk
mirhagk commented Feb 13, 2015

The community is far more than the 10 or so people who contribute to a GitHub discussion.

I think this is the important thing. From a general perspective it shouldn't be too difficult to switch the compiler from a Environment.FailFast to throwing exceptions. I think that it should be implemented as Environment.FailFast and given to the community to play with. If the community at large has an issue with it then it can be changed before it's actually released.

As much as I think the failfast won't work, I do think that should be the approach going into C# 7 previews. Only once the community at large plays with it will we know if it won't work. And if it won't work for most people (which most people in this thread are guessing it won't) then it should be switched before the actual release. The language team has been very good at listening to the community and switching implementations before release (like how string interpolation is switching before final release).

A reliable extension point where developers could record failures that trigger Enviroment.FailFast.

This actually could solve a lot of the issues. Give the application the chance to use it's own logging, attempt to save the application state for the startup code to attempt to recover etc. Perhaps it could even be a way to abort the fail fast and turn it into an exception. That might be a little confusing but then it makes it possible for a testing framework to catch the code. I don't know if it'd work well, but I'd like to give it a shot.

@eatdrinksleepcode

@jaredpar Having previously worked on Code Contracts, you are surely aware of the massive debate that went on there over whether or not to fail fast. The Code Contracts team tried for months (years?) to convince the community that failing fast was the right thing to do, but were ultimately unsuccessful. Having the opportunity to feel the effects of such a misguided decision, I certainly did not embrace it. I was one of those decrying the absurdity of fail fast then, and will continue to do so now.

You haven't responded to any of the objections brought up in this thread:

  1. How do you do error logging? Watson is simply not sufficient; the vast majority of .NET applications don't use it because of the limited and arcane information it provides, and the difficult in accessing that information. EVERY .NET application I have ever seen produces its own error log.
  2. Is it appropriate to take down a production web server serving millions of users around the world because of an innocuous logic bug in a corner case for one particular user?
  3. What happens when a unit test violates a contract? Crash the unit test runner?
  4. If a programming error should always instantly crash the process, why does every other error condition throughout .NET thrown an exception? Why does NullReferenceException even exist: shouldn't the process just die? Why does failing to JIT a method (which surely indicates a much more serious problem than a contract violation) throw an exception instead of killing the process?

I believe strongly in surfacing errors quickly and not hiding problems where they can fester. But insta-killing the process is not the only way to quickly surface errors, and it eliminates all other potential options.

Frankly, it is extremely condescending to continually assert that if everyone would just try it your way, they would see the light and realize that you were correct all along. Assuming that anyone who disagrees with you must be less knowledgeable or experienced than you gives you an excuse to ignore them and leaves you blind to the possibility that you might be wrong. The point of opening up the development process on a public forum like GitHub is not so you can read the concerns of the community and then write them off.

I am a smart person, and there are a lot of other people who are smarter than I am who are passionate about building a better language that lets us build better software. Don't write us off, convince us; and if you can't convince us, consider the possibility that you might have missed something, and be willing to reconsider your own position.

@MgSam
MgSam commented Feb 13, 2015

@jaredpar So you consider a "success" to be an application where the only way to tell what went wrong is to look in Windows error logs? In my business we consider that to be a poorly written application that is difficult to debug. We use actual log files and developer-friendly error reporting. Could you give any examples of "real" applications that benefited from such a system?

Although I think this is a bad idea even for everyday applications- it's especially ludicrous when you consider mission critical systems. I can just imagine someone trying to tell their boss their trading system crashed and they've been off the market for an hour because of this great new "feature" some academic in Redmond decided to add to the language. Or how about the doctor telling his dead patient's relatives that their pacemaker stopped working because someone asserted February had 28 days? There is an endless list of examples that you could give as to why this is such a bad idea.

Also- yes the community is more than the 10 people who contribute on Github, but the people who do contribute there are C# enthusiasts who likely have a higher-than-normal tolerance for new language features. If we all think its a bad idea I can only imagine what the average Joe developer would think. It's also incredibly insulting for you to be so dismissive of the community's opinions- that's pretty much the opposite of what open source development is supposed to be about.

Bottom line- if this feature made it into the language as proposed I would use (or write myself) an analyzer to ensure that it is not used anywhere in our codebase. We're not writing academic software. We need to handle exceptional conditions with thorough logging and error reporting.

@HaloFour

@mirhagk

I think this is the important thing. From a general perspective it shouldn't be too difficult to switch the compiler from a Environment.FailFast to throwing exceptions. I think that it should be implemented as Environment.FailFast and given to the community to play with. If the community at large has an issue with it then it can be changed before it's actually released.

That's fair. Drop a build, post a feature list explicitly layout out the behavior on MSDN magazine (especially mentioning the fail-fast behavior), and let the flame-fest fly. I do think that the gut reaction of the vast majority of developers will be the same as mine (and seemingly everyone else on this thread thus far). If I'm wrong then I will gladly shut up and let the community have what it wants without objection.

@mirhagk
mirhagk commented Feb 13, 2015

I do think that the gut reaction of the vast majority of developers will be the same as mine (and seemingly everyone else on this thread thus far). If I'm wrong then I will gladly shut up and let the community have what it wants without objection.

I 100% agree. But I also think that it's not enough to assume that we (github followers of the repo) are the typical developer community for .NET or to assume what they will want based on what we want. I know that I stand out very much at my workplace, and while I can predict my workplace's reaction to this feature using failfast (we wouldn't use contracts in that case) I can't predict those outside my workplace.

I think the tricky part is getting their feedback. Most C# developers aren't even aware of C# 6 changes yet (and the majority I've seen don't know how to use C# 5 features effectively). It's going to be tricky getting their feedback (not just for this issue either, in general any new feature). One way is to put something on uservoice and share the link on popular programming communities (like reddit and hackernews), another is to simply create the feature for the preview, and get some prominent C# authors to blog about it. Those will make their way to things like The Morning Brew, and ultimately gather a lot of the community.

@jaredpar
Member

@eatdrinksleepcode

How do you do error logging? Watson is simply not sufficient; the vast majority of .NET applications don't use it because of the limited and arcane information it provides, and the difficult in accessing that information. EVERY .NET application I have ever seen produces its own error log.

I disagree with the classification of Watson. I've used it to track down countless bugs over the years. Yes it is arcane and cryptic but it's also extremely useful.

As for logging specifically the only item in question would be logging of the actual failure. Everything up to that point would proceed unaltered.

Variants of the design in the past have discussed having a terminate routine. Essentially a call back that runs on a contract failure which allows for items like more detailed logging, emails, etc ... to occur. There are some gory details around this function but I've always felt it was a good compromise.

Is it appropriate to take down a production web server serving millions of users around the world because of an innocuous logic bug in a corner case for one particular user?

Is it appropriate to reveal the personal information of millions of users around the world because the server failed to shut down when it detected a programming bug?

What happens when a unit test violates a contract? Crash the unit test runner?

Unit testing is definitely the trickiest aspect to handle. There are a couple of strategies that I've seen tried here.

One is to simply isolate contract tests on a per process basis. This works, requires no compromises to the model but is impractical for real world use. Especially on Windows where processes tend to be a bit heavier weight.

Another is to leverage the idea of the terminate routine is with logging. The test framework adds a helper in the flavor of Assert.FailsPrecondition(Action action). Under the hood it manipulates the terminate routine to set a flag noting it was hit and thrown an exception that the implementation of FailsPrecondition catches.

Allowing the terminate routine to continue execution by throwing or setting a flag does make me squirm in my seat a bit. But it's a practical compromise for a couple of scenarios of which unit testing is the most prominent.

If a programming error should always instantly crash the process, why does every other error condition throughout .NET thrown an exception? Why does NullReferenceException even exist: shouldn't the process just die?

I agree these are very much analogous to contract failures: predictable conditions which when triggered represent coding errors in the program.

Should these failures trigger fail fast? In my opinion yes. I've never seen a case where I wanted a program to continue executing after an NRE and seen many cases where the continued execution after the NRE lead to must worse and hard to track down bugs. But this is a runtime decision.

I do understand that developers want to have an option of throwing here because it's the way .NET typically handles this situation. This is ultimately why I think the option of else throw will be a part of the design (if contracts are taken at all). It is a nice unification and back compat story for the feature.

Frankly, it is extremely condescending to continually assert that if everyone would just try it your way, they would see the light and realize that you were correct all along.

I don't mean that to be condescending and I apologize that my writing is coming off that way. I mean that as a genuine piece of advice / recommendation. As someone who was very skeptical of ti at first my advice to others is to try it.

I am a smart person, and there are a lot of other people who are smarter than I am who are passionate about building a better language that lets us build better software. Don't write us off, convince us; and if you can't convince us, consider the possibility that you might have missed something, and be willing to reconsider your own position.

I'm definitely not writing anyone off. Unfortunately though I'm not sure what to do in terms of convincing others except to ask them to try it.

I started out as a skeptic of fail fast in the beginning and at the time I'm not sure what anyone could have said to convince me it was a good idea. I was junior enough at the time that I didn't really factor into the decision process for the team. It was more or less a decision that was forced on me. Had it not been I may not have ever embraced it the way I have.

@tomasr
tomasr commented Feb 13, 2015

@jaredpar I have no opinion one way of the other at this point.I do think in many scenarios, failing fast is the right thing to do, as such a failure clearly points to developer error.

However... there's gotta be a compromise into how to figure that out. I have to agree with some of the arguments made by other people in that sometimes those of us inside of corp use for troubleshooting is not necessarily what our customers out there use (or are in a position to use). DrWatson is fantastic, but I've only met a couple of people that even knew it existed.

Crash dump analysis is fantastic and I thoroughly enjoy it, but it's a black art as far as a huge chunk of our user base concerns. And unfortunately, I do see lots of applications in production throwing random ArgumentExceptions in production, to know that this would happen with contracts just as well :(

@mirhagk
mirhagk commented Feb 13, 2015

Let's discuss the approach of Environment.FailFast but with calling some sort of logging callback. Should this be an event that is raised with code that wants to do something subscribing to the event? Should the event allow transforming the call into an exception instead? (My opinion on these is yes for both, to enable the discussed scenarios).

I think the event should allow transformation, but I also think this should produce a warning. There are circumstances where this is necessary, but in general the correct practice is to log the error and exit the application. The exceptions I can think of are things like testing frameworks and web servers, where you'd want to kill the code that's running, but not kill the entire process (i.e. kill the incoming request, refuse to respond, but continue allowing requests for the rest of the site that doesn't have the bug).

@jaredpar
Member

@tomasr

I agree that compromise is necessary and the most likely path for this feature to be accepted. The summary of where I think this should go is:

  • requires <expr>: triggers terminate routine on contract failure
  • requires <expr> else throw <expr>: triggers the throw clause on contract failure
  • Contract.Failed: event / callback which is invoked on failures of the first contract variety. Default behavior is to use Environment.FailFast. Can be overridden to cancel process termination.
  • ensures variety for both of the above requires

Note: the name of Contract.Failed would almost certainly be different than that. I used it as a place holder for the discussion, not an actual suggestion of the name.

@tomasr
tomasr commented Feb 13, 2015

@jaredpar that sounds pretty reasonable to me. My only concern is having to deal with yet another "unhandled error, process will terminate" event (mostly that developers will need to be aware of it, remember to hook it up, which almost represents a silent behavior change for ported code), and not entirely sure how that would play with platform-specific termination routines (say, ASP.NET).

@RichiCoder1

@jaredpar Sounds reasonable to me too.

Another random though that would be nice is the ability to evaluate a methods contract against a set of arguments. Something like Contract.Test(MyMethodInfo, params): boolean.

Edit: A "?" there would be the behavior of Contract.Failed if requires <expr> else throws <expr> or ensures is used. Would/should they just locally override the Contract.Failed behavior?

@tomasr I think it would kind of fit into the pay to play mentality. If you want to use contracts, you have to recognize all the cases that comes with that.

@tomasr
tomasr commented Feb 13, 2015

@RichiCoder1 don't disagree but problem is that the one paying might not be the one choosing to play in the first place :)

@ilmax
ilmax commented Feb 13, 2015

@jaredpar I think that give the end user (we, the developers) the ability to choose is a good compromise. I prefer the exception behavior over the fail fast, and I appreciate the ability to choose what happens when a contract is violated.

@jaredpar
Member

@RichiCoder1

You actually hit on a really important scenario there and it does need to be added. The scenarios for it are a bit more corner case (client server architectures in particular) but once you get into that scenario it's a must have.

@vkhorikov

In terms of http application input validation. Please take a look at my blog post: Code contracts vs input validation

@jaredpar

  1. I think you should leave us (developers) a right to shoot our leg. If we want to throw an exception - let it be. It would be nice to have a choice between exceptions and Environment.FailFast.

  2. It seems like Contract.Failed event makes requires <expr> else throw <expr> syntax redundant in most cases. Do you think there will be cases where using different precondition behaviors (i.e. requires <expr> else throw <expr> and requires <expr>) would be justified? I think they will be used either for throwing exceptions or crashing the app, not both. Also, using different syntax complicates switching between these behaviors.

  3. It would be nice if the preconditions will be able to call pure methods to make some more complex validation:

public void DoSmth(int param)
  requires IsValid(param)
{ ... }
@HaloFour

@vkhorikov I agree that there is a distinct difference between code contracts and input validation and in a wholly closed system the former absolutely represents a bug. However most systems aren't fully closed, they are built from many disparate components.

As a library maintainer I basically need the equivalent of input validation but at the method level. I don't want to fail-fast because frankly that's not my decision to make. If that library is used in a web service or the like a fail-fast will bring down the application pool, immediately killing every other request in flight and requiring that the application pool be relaunched, which is usually an expensive process. A simple error would result in a very effective DoS vector. Worse, 99.999% of the web site developers who would face this issue would have no clue how to deal with it. Simply put, "my library" would be deemed flaky and people would stop using it until I got rid of this "fragile" validation mechanism.

I want code contracts. I want simplified argument validation. I want metadata generated from that validation that can be used by analyzers to catch potential invalid calls in the IDE, long before that invalid code makes it into production. I don't want argument validation that actually requires more code than the simple if statements that we write today, or the helper methods that everyone under the sun has written. I don't want to have to waste my time trying to convince other developers that the dark art of code dump debugging is the appropriate way to troubleshoot invalid arguments. It's difficult enough to get them to read the logs that IIS so thoughtfully already generates when these exceptions go unhandled.

The precedent has long since been set in previous languages and throughout the BCL. Runtime and validation exceptions exist for a reason and they are the appropriate route to go. If there is all of this concern about "swallowed exceptions" then the C# team should just make it impossible to catch any form of runtime exception because graceful degradation is clearly a horrific idea. And yes, I will remain pretty vehement about it, especially in the absolute absence of a justification, because this is a feature that I've wanted for years, but not in this esoteric and useless form.

@vkhorikov

@HaloFour
I totally agree with all you have said. Your arguments are actually the reason why I am too asking @jaredpar to preserve our right to choose between throwing exceptions and crashing the app. I would also add one more: unit tests. If the app will crash every time a contract is broken, it would be extremely hard to test them. Whether to crash the app or swallow the exception should be decided by the app developers, not by .Net architects.

Nevertheless, what you are writing about in terms of the library you are maintaining is a contract. Your point is that you don't want the client app to crash and it is a totally valid point because, again, it should be our choice, not .Net architects'.

We use exceptions for both contract violation and other exceptional situations, but not all exceptions signalize a bug. Contract violation exceptions should be treated differently. While "regular" exceptional situations (like connectivity issues or I/O problems) can be easily ignored by developers after the cause was removed, contract violation exceptions should be carefully investigated and the code generating them fixed.

@paulomorgado

I think that the biggest problem here is a disconnect between what @jaredpar thinks of a code contract and what others think of a code contract.

My understanding of @jaredpar 's view of code contracts is that it's an extension of the compiler (although he states the they should be proven by other tools). Thinking of all contract types beyond parameter validation, I'd have to agree (although I think that, if it's in the language, it should be verified by the compiler).

My understanding of the everyone else's view is that it's just function parameter validation.

Another disconnect I see is the difference between "I (the function) can deal with your bad input and throw it in your face" and @jaredpar 's "I the system ...".

@jaredpar 's view writing does come across a bit academic and that triggered a lot of "I, in the real world" reactions.

Although I've written applications that tried to survive whatever errors I introduced in it, because it was the requirement that they do (keep working, not having errors), they weren't dealing with any important data. When dealing with important data, I have chosen to fail fast many times.

I consider @jaredpar 's last proposal very reasonable and a good working draft of what the final version should be.

@HaloFour

I think we absolutely have a disconnect. The original proposal seems to have evolved from what was done in MSR involving Code Contracts which was more of an academic process and the idea there was that the appropriate course of action was to fail fast. Of course what shipped as Code Contracts in Visual Studio doesn't behave that way, it still throws exceptions unless you go out of your way to wire up the ContractFailed event to manually fail fast.

Thing is, I don't think that it's appropriate for the actual code contract to define what happens when it is deemed invalid. Doing so removes that choice from whomever and whatever is consuming that code. I think the contract should just describe the rules and nothing more. I think that the application itself should then define what the behavior should be for a failed contract. I'm even fine if fail-fast is the default behavior, at least of release build applications. But I think that any application should be able to change that behavior to something between fail-fast and throwing exceptions, as well as have different options for logging the validation. And I think that if Microsoft is about to unleash something that will catastrophically detonate people's processes that they better both ready a whole suite of "Core Dump Troubleshooting for Dummies" tools as well as prepare the Microsoft support team to steel themselves for the onslaught of support requests to figure out why this framework is so buggy and fragile.

When I think of a feature like "code contracts" my mind immediately jumps to argument validation. Why? It's an insanely common problem and one that developers frequently shy away from because it's tedious and boring to write a bunch of manual checks. Like many others I have my own library, which I rolled long ago, which rivals even Code Contracts in terms of how succinct it is to use. What my library doesn't do, and the one thing that I severely want it to be able to do, is to tie into a larger static analysis framework to provide better compiler support for detecting when an invalid call might be made so that these types of issues are caught earlier. It's the one aspect of this proposal which is painfully absent.

I'll throw another scenario out there: SQLCLR. I actually have written and maintain a number of SQLCLR libraries. Thankfully the host won't permit failing fast. An assembly that tried to use code contracts would probably be considered unloadable.

@paulomorgado

@HaloFour, once again, there's a big difference between "my system" and "my code/library".

I agree that, in most cases, fail fast is the right way to go. I would also argue that an SQLCLR library should be one of those since it's touching my data.

I'm also living in the real world, which means I have to take a lot of crap. But that doesn't mean that I agree with it or that I'll stop fighting it.

For those that ultimately disagree with @jaredpar, I sincerely hope that you'll never be in a position of "Jared was right!".

@jaredpar, I think your last proposal is more realistic without straying away from the correct course of action. But i do think the compiler should do something other than generating code. Admittedly, more complex theorem provers might be needed, but the compiler should provide a first level of validation.

@fschmied

@jaredpar And now for something completely different - your proposal seems to assume that integrating with System.Diagnostics.Contracts is not desirable. However, I'd like to raise the concern that it will be quite confusing (and probably surprising) to have two different variants of code contracts on the same platform.

I'd therefore propose that you reconsider reusing Contract.Requires and Contract.Ensures (i.e., have the C# compiler generate calls to those methods) and either

  • change the default behavior of those methods to match the desired behavior of C# code contracts (breaking change), or
  • add overloads/new methods to the Contractclass that implement the new desired behavior of C# code contracts.
@HaloFour

@paulomorgado

I would also argue that an SQLCLR library should be one of those since it's touching my data.

So you think that the entire SQL Server service process should immediately die because someone passed a NULL to a stored procedure parameter that shouldn't be permitted to accept it? That all other databases in use should be immediately taken offline? That all operations in flight discarded and databases in flux marked suspect and require a potentially lengthy recovery operation? That's ridiculous. Fortunately the SQLCLR team made such calls impossible, and for that very reason.

@vladd
vladd commented Feb 16, 2015

@HaloFour
I don't think contracts are the same as parameter checking. For parameter checking, one already has the canonical if (param == null) throw new ArgumentNullException().
I would see contracts as a kind of guarantee. If you are sure that your method cannot possibly be called with param == null, you add a contract. If you've got a public method, the client is allowed to call it with whatever arguments he wants. But if you control the method visibility (say, the method is internal), and therefore control all calls to it, than you can be sure that your method is called with non-null arguments (and sometimes the compiler even can prove that).
So your example doesn't apply here.
(And I am a strong proponent of not suppressing bugs but crashing as soon as possible, otherwise the program is anyway in an inconsistent state which can result in a massive damage.)

@HaloFour

@vladd In which case we would be talking about two different features. However, that would still lead to lots of confusion as practically the first thing any code contract library mentions, including that from Microsoft Research, is argument validation and API documentation. Even purely for internal purposes fail-fast is not the precedent (not even by the existing facilities provided by Microsoft) and won't be the expected behavior.

The canonical argument validation method only covers the simplest of cases. Even in your example you're not providing enough information and should be passing nameof(param) to ArgumentNullException. Range checks and other simple forms of argument validation have no built-in facilities in the BCL meaning that short of rolling your own you'll get no localization of error messages. And of course none of this feeds any form of metadata that can be used to automatically document the method nor provide any form of static analysis.

Anyway I'm about done flogging this horse. I agree that this needs wider discussion as we're dealing with much too small of a sample set on these forums. When something about C# 7.0 eventually hits MSDN Magazine I hope it contains, in no uncertain terms, the exact behavior to be expected and the rationale behind it.

@RichiCoder1

@fschmied I could be wrong, but I believe the intention would be to simply deprecate System.Diagnostics.Contracts.

@HaloFour @vladd I agree that SQLCLR should fail fast if a contract is broken, because if it doesn't, that puts your data at risk. But that again goes back to Contracts vs. Validation and sanitation. I however agree that the behavior of this feature, and any other features that evolve out of this, should be shouted to the high heavens to ensure (sorry) that people know exactly what they're getting. I also agree the debugging and testing story should definitely be very thoroughly thought out.

@HaloFour

@RichiCoder1 The operation should fail-fast. The process of the database server? No way. You'd have a lot of really pissed off DBAs if that were the case. But it's kind of moot because I'm pretty sure that SQL Server will not load any assemblies that use Environment.FailFast.

This is just another example of user assemblies loaded into larger infrastructures where the behavior of contract validation failures should be controlled by an external actor and not decided strictly by the compiler. If the implementation would rely on a potential event allowing for said infrastructural code to handle the failure its own way (e.g. log, tear down AppDomain, etc.) as mentioned by @jaredpar then I'm fine with it.

@jaredpar
Member

@paulomorgado

But i do think the compiler should do something other than generating code. Admittedly, more complex theorem provers might be needed, but the compiler should provide a first level of validation.

I think the correct role of the compiler here is to enable theorem provers to operate on contracts vs. being a theorem prover itself.

Theorem proving is a very difficult problem space. The solutions are constantly evolving and are often very computationally expensive even for seemingly simple code snippets. The execution time for the proof can quickly exceed what is considered reasonable for the code being compiled.

The rapidly evolving nature of the algorithms and potential bad turn around time make it unsuitable for a "in the box" compiler feature IMO. Instead I think the compiler should generate contracts in such a way that it is recoverable by theorem provers after the fact. Essentially move it into a post compilation processing step on a more moderated basis where turn around time is less critical (nightly, weekly, etc ...).

@vladd
vladd commented Feb 16, 2015

@HaloFour
After some more thinking on your example: any process should allow only trusted code to run within itself. For a DB process, catching exceptions while running malfunctioning code isn't a way out of problems. An ill-behaving code might as well deadlock, block resources, never return or whatever.
If the code is seen as a foreign, potentially malfunctioning one, it must be run in a separate AppDomain, and torn off as a whole is something goes wrong. If the code is trusted, then failing to fulfil the contract is a bug and must be detected and fixed ASAP. Catching contract failure is IMHO something like catching IndexOutOfBoundException.

@RichiCoder1

@vladd Agreed. Brings in the whole idea that if there's a risk outside data could cause a contract failure, it should be isolated so it and everything it's touched collapses without affecting core functions. So a query that could cause database corruption would just sink the processing domain rather than the entire database. Again, assumes Contracts are a hard stop mechanism rather than validation though

@HaloFour

@vladd , @RichiCoder1

Exactly. My opinion is that the behavior as to how to handle a contract failure belongs to whatever is managing that AppDomain. Using AppDomain isolation for untrusted code is appropriate, but that's still code within the same operating system process and Environment.FailFast will tear down all of those AppDomains immediately. In the case of SQLCLR it goes even further as the SQL Server process runs its own CLR host which severely limits what any assemblies are permitted to do within them, but it's still entirely in-process.

@gafter gafter assigned gafter and unassigned MadsTorgersen Jul 25, 2015
@dsaf
dsaf commented Jul 31, 2015

@rhnatiuk Valid point, strange people are ignoring it. I mean, it's basically implemented, here is a tutorial:

http://www.codeplex.com/Download?ProjectName=specsharp&DownloadId=84056

It has some nice samples, demonstrating various contracts:

image

It would be great if the team could comment on how Spec# is different from what is suggested. Will the new implementation be feature-complete, how will it evolve etc. Will loop and class invariants be supported in future etc.

https://en.wikipedia.org/wiki/Loop_invariant
https://en.wikipedia.org/wiki/Class_invariant

@rhnatiuk

@dsaf No idea. But it seems to be hopeless: I do not believe that large and complex things, like contracts, can be designed by community, and this thread is proof of that - way too long, way too many conflicting opinions, completely insane looking proposals, etc. It is shame, that Microsoft killed Spec# when it was basically done.

@dsaf
dsaf commented Jul 31, 2015

@rhnatiuk
> It is shame, that Microsoft killed Spec# when it was basically done.

It happened to multiple experimental forks though and it can be hard to predict what should be API/syntax:

  1. Axum:
    -- language - killed;
    -- API (Orleans) - failed;
  2. Cw:
    -- language (query syntax) - failed;
    -- API (LINQ) - succeeded;
    3) Polyphonic C#:
    -- language (async/await) - succeeded.
    (correction - no relation between them)

Design by contract is different though, because it had been used by a few mature general-purpose languages already e.g. D. Well, at least the "safer" API contracts approach had now been tested and largely dismissed as impractical and inferior. I guess Roslyn will make managing the compiler code complexity easier and now might be the best time to merge Spec# into the main language.

Even the Rust community is interested in learning from Spec#: http://www.reddit.com/r/rust/comments/2ys3ft/what_about_design_by_contract_like_spec

Perhaps @mike-barnett could shed the light on feasibility of merging Spec# into Roslyn and why they had went the API way in past.

@gafter
Member
gafter commented Jul 31, 2015

@dsaf I don't see the relationship between Polyphonic C# and async/await.

@dsaf
dsaf commented Jul 31, 2015

@gafter Sorry, it's a mistake. I was confused by it having an async keyword.

@HaloFour

@dsaf @gafter

#1165 I do think that we should bring those ideas from Polyphonic-C# and Cω into C# 😄

@mike-barnett

Technically, there is no reason contracts couldn't get added to Roslyn. We took the API route for Code Contracts because we had already created our own language with contracts (Spec#) and it somehow failed to take over the world. We wanted to see what kind of adoption we would see if we made it available in any .NET language. Of course, we had to give up quite a bit in that process (e.g., the non-null type system).

@rhnatiuk
rhnatiuk commented Aug 1, 2015

@mike-barnett, how something not released (not even alpha!) can "take over the world"? :) What Microsoft did with contracts by introducing them via API was utterly pathetic, was defying the whole point of code contracts (i.e. contracts being part of the exposed API; in MS implementation nothing in signatures hints what are the contracts), and thus useless. Spec# was much closer to bring real contracts, than anything else MS ever made.

@AdamSobieski

Discussion

Excellent ideas, excellent discussion.

Type System, Reflection and Lambda Expressions

Each precondition, postcondition or effect, or constraint, can be viewed as equivalent to the body of a lambda expression with at least the parameters of the method, input as well as output, each such lambda expression returning Boolean. It seems that there are a few more expression tree node types to obtain the entirety of the semantics, the topics pertinent to LINQ as well as to Roslyn.

LINQ and Roslyn arrived after the type system, reflection system as well as generics system. Though the discussion, thus far, is about C#, we can also envision versioning the .NET CLI, which could provide non-null, versioned type system, reflection system as well as lambda expressions.

public partial class Type
{
  public ReadOnlyCollection<LambdaExpression> GetGenericParameterConstraints() { ... }
  public LambdaExpression GetInvariant() { ... }
}
public partial class MethodInfo
{
  public ReadOnlyCollection<LambdaExpression> GetRequires() { ... }
  public ReadOnlyCollection<LambdaExpression> GetEnsures() { ... }
}
public partial class LambdaExpression
{
  public ReadOnlyCollection<LambdaExpression> GetRequires() { ... }
  public ReadOnlyCollection<LambdaExpression> GetEnsures() { ... }
}

(Note: Such functionality could also be implemented as extension methods.)

Parametric Polymorphism

Topics include the structure of parametrically polymorphic lambda expressions for generic method definitions or type definitions, or the semantics of the requires or ensures on generic method or type definitions, the interplay between parametric polymorphism and design by contract, the use of parametrically typed parameters, elements, parametrically polymorphic method calls or the type parameters in requires or ensures statements.

As lambda expressions are indicated as of use for the parametrically polymorphic methods and types, LambdaExpression could be parametrically polymorphic:

public partial class LambdaExpression
{
  public bool ContainsGenericParameters { get { ... } }
  public bool IsGenericLambdaExpression { get { ... } }
  public bool IsGenericLambdaExpressionDefinition { get { ... } }
  public ReadOnlyCollection<LambdaExpression> GetGenericParameterConstraints() { ... }
  public LambdaExpression GetGenericLambdaExpressionDefinition() { ... }
  public LambdaExpression MakeGenericLambdaExpression(Type[] typeParameters) { ... }

  public ReadOnlyCollection<LambdaExpression> GetRequires() { ... }
  public ReadOnlyCollection<LambdaExpression> GetEnsures() { ... }
}

Lambda Expressions and Sets

Preconditions, postconditions or effects, or constraints, constraint logic programming systems, can utilize lambda expressions returning type Boolean. I'd like to present a computational approach to sets, where parameter constraints, aforementioned, can be viewed as resulting in described subsets.

A view of a computational set theory:

public partial class Set
{
  // see also: Expression<Func<object[], bool>>
  private Expression<Func<object, bool>> m_lambda;
  private Func<object, bool> m_compiled;

  public Expression<Func<object, bool>> Expression { get { return m_lambda; } }
  public bool Contains(object value) { return m_compiled(value); }
}

shows a fundamental relationship between set theory and the theory of computation. Preconditions or parameter constraints upon methods' parameters can be viewed as intersecting with, subsetting, the Cartesian products of methods' parameters' sets.

The topics pertain to language features, specifically multiple types or methods which differ in the constraints on their parameters, as the expressiveness of constraints systems move towards that of the languages, as per lambda expressions (see also: C++ templates system).

Compile-time and Runtime Method Binding, Type Binding and Type Inference

I would like to express interest in compile-time and runtime method and type binding, compile-time and runtime type inference, including for multiple types and methods which differ in terms of constraints on parameters.

Code Contracts Engine API and Logic Programming

I would like to express interest in the interoperability between runtime and static program analysis and logic programming.

I would like to express interest in API to running code contracts engines, resembling System.Diagnostics.Debugger for scenarios including programming language design, IDE features or IDE extensions such as language services (e.g. logic programming languages). Such API could resemble that of knowledgebases, possibly utilizing Expression, of use, for instance, for expanding the logical rule systems of the engines. We can envision various Visual Studio 2015 features for logic programming languages. The System.Diagnostics.Contracts.Contract static methods could expand to include methods more resembling a logic programming API.

Scopes

Resembling InvocationExpression or inline expansion scenarios, scoping:

{
  requires
  ensures
  ...
  {
    requires
    ensures
    ...
  }
  ...
  {
    requires
    ensures
    ...
  }
  ...
}

The System.Diagnostics.Contracts.Contract static methods could expand to include scoping.

using(var scope = Contracts.Scope(/*...*/))
{
   ...
}
@mike-barnett

Sorry, I was being glib there. It is difficult to get people to use a new language or even an experimental compiler for an existing language (since Spec# was a strict superset of C# 2.0). We also did not have the resources to keep updating it to remain a superset as C# evolved. We totally understood the limitations inherent in a library (API) solution as opposed to a language extension, but engineering is nothing other than dealing with such tradeoffs. We produced as much tooling as possible to make up for the limitations: for instance, if you install the Code Contracts Editor Extensions, then the contracts do indeed show up in the method signatures shown by Intellisense. We are sorry if you found our work to be pathetic.

@rhnatiuk
rhnatiuk commented Aug 3, 2015

@mike-barnett , please do not misunderstand me - I did not mean that as an offense! Developers at Microsoft did great job on tooling and related things supporting CodeContracts!

What I call "pathetic" here, is that Microsoft had absolutely fantastic idea with Spec#, working proof of concept, and even some public releases, and then Microsoft (organization, I believe in this case!), because of politics, schedule, whatever-other-irrelevant-wrong-reason-we-bring-here, dumped all that, and came up with half-thought and half-baked CodeContracts via API that no one uses, because they do not solve any real problems people have, or solve them in a completely wrong way.

I think Microsoft and its R&D fell into the same trap they very frequently fall into with its products and technologies (I hope you believe me on my word here - the list would be too long to write): do something that technically CAN be done, without really thinking what is the real PROBLEM, what is NEEDED to solve it, and what HAS to be done.

Code contracts serve two masters, so to speak. First of all, they must be clearly visible and understandable by developers (without the need to read whole function body, searching for contracts, or relying on tooling, e.g. Intellisense in your example), and only then they must be used by compilers and runtime environments to verify the code. CodeContracts via API serve only the second one, while Spec# was doing both excellently.

@dsaf
dsaf commented Aug 3, 2015

...if you install the Code Contracts Editor Extensions, then the contracts do indeed show up in the method signatures shown by Intellisense...
...
First of all, they must be clearly visible and understandable by developers...

Visual Studio is lacking a proper custom attributes support, it's true and a general issue #711.

@aarondandy

I wonder if ensures could be made to work naturally with async method results.

It would be really nice if this could be worked in but it may be a bit awkward. I assume the ensures would only be expected to hold in situations where the task completed successfully but that seems like it could get complicated. This is definitely something that we should be thinking about though.

@aarondandy

Would it be feasible to just try this feature out? Code Contracts is starting to release early 2015 support so couldn't a roslyn extension be used to replace ccrewrite to try variations of this syntax out? Is this a crazy idea? I know it would be a lot of work but when you think about a future where this language feature actually exists Code Contracts should have some kind of interoperability with it anyway.

@gafter
Member
gafter commented Aug 8, 2015

@aarondandy We don't have a prototype of this feature at this time, but we'd welcome the opportunity to review a contributed prototype. Otherwise I'm afraid you'll have to wait quite some time for us to prepare one.

@gafter gafter assigned jaredpar and unassigned gafter Aug 8, 2015
@gafter gafter added this to the C# 7 and VB 15 milestone Aug 8, 2015
@eyalsk
eyalsk commented Aug 17, 2015

Will this suggestion integrates with APIs that exist in the framework today like Contract.Requires(...) and Contract.Ensures(...) or we will have to modify the code for the compiler to understand the contracts?

Sorry if it was answered before but it's a huge thread so I might have missed that. :)

@dsaf
dsaf commented Aug 17, 2015

@eyalsk

Sorry if it was answered before but it's a huge thread so I might have missed that. :)

The answer is in the first two paragraphs of the opening post :).

Will this suggestion integrates with APIs that exist in the framework today like Contract.Requires(...) and Contract.Ensures(...)

A proper flexible language-level support was proposed, not an API integration.

or we will have to modify the code for the compiler to understand the contracts?

Yes. It's a transition from "API + specific IDE Add-in" to "language + any modern IDE". I would imagine something that works in VS Online, VS Code and MonoDevelop in a performant way is the target.

@jaredpar said:

This language proposal is not an attempt to integrate all of those features into the language. Instead it is focused on a declarative and succinct form of argument validation.

A side goal of this feature though is to allow for tools like the CC addition to be more easily constructed. Having declarative contracts removes the need to grovel through IL to infer contracts based on heuristics and instead just hands the contract directly to the consumer.

@eyalsk
eyalsk commented Aug 17, 2015

@dsaf, haha.. I'm funny but thanks!

I know it's a proposal and that syntactically it makes sense to have the contracts as part of the method's signature but it looks awful especially for many statements.

Many contracts can easily clutter the code and make it unpleasant to read/maintain it but I would imagine that you will allow us to put them on interfaces so it won't be much of a problem and this is one reason why it makes sense to put them at the signature level as opposed to the body of the method.

@AdamSobieski

Syntax

Interfaces

On the topic of interface syntax, the method body syntax includes convenience for accessors. For readability, the new syntactic elements could be the first statements in scopes either syntactically or stylistically (see also: https://github.com/DotNetAnalyzers).

public interface Interface
{
  public void Function(...)
  {
    requires ...
    ensures ...
    ...
  }
  public float Scalar
  {
    get
    {
      requires ...
      ensures ...
      ...
    }
    set
    {
      requires ...
      ensures ...
      ...
    }
  }
}
@dsaf
dsaf commented Aug 18, 2015

@AdamSobieski can something be added between { and requires or ensures? This doesn't feel right to me regardless.

@GeirGrusom

There already is a precedence for this kind of syntax in generics.

@AdamSobieski

Syntax

Interfaces

The new statements could be throughout scopes but, beyond the readability topics, topics would include compiling, running (see also: F11, https://msdn.microsoft.com/en-us/library/y740d9d3.aspx), semantics as well as the processing of the statements statically, processing the statements' expression trees. Often, statements appearing before are to construct the parameters, expressions which compile to Booleans.

"This method call [Contract.Requires(bool)] must be at the beginning of a method or property, before any other code." (https://msdn.microsoft.com/en-us/library/dd412847(v=vs.110).aspx)

"This method call [Contract.Ensures(bool)] must be at the beginning of a method or property, before any other code." (https://msdn.microsoft.com/en-us/library/dd412868(v=vs.110).aspx)

Virtual Methods on Interfaces

Also interesting is what else could be in the { } on interfaces, e.g. default methods or, instead of abstract methods, virtual methods, on interfaces.

@whoisj
whoisj commented Aug 18, 2015

Assuming we get contracts at all, let alone on interfaces. Perhaps the syntax should be more like:

public interface Interface
{
  public void Function(...)
    requires ...
    ensures ...;
  public float Scalar
  {
    get ensures ...;
    set requires ...;
  }
}
@HaloFour

@whoisj

Makes sense, otherwise if we get both interface contracts as well as default methods you'll end up with confusing syntax. The where clause for generic constraints provides some precedence to that form of syntax.

@AdamSpeight2008
Contributor

What about the syntax from #129?

@eyalsk
eyalsk commented Aug 18, 2015

@AdamSpeight2008 I don't know to what part in the syntax of traits you refer to but one thing is clear it isn't crystal clear as having requires and ensures.

Can you give an example of how the syntax of traits will have a play here? :)

@AdamSobieski

Discussion

@stephentoub presented:

public int Insert(T item, int index)
    requires index >= 0 && index <= Count
    ensures return >= 0 && return < Count
{
    return InsertCore(item, index);
}

@whoisj presented:

public interface Interface
{
  public void Function(...)
    requires ...
    ensures ...;
  public float Scalar
  {
    get ensures ...;
    set requires ...;
  }
}

Regardless of the syntax, the new features as well as expanding the expressiveness of C# are exciting.

Scopes

On the topic of scoping (#119 (comment) § Scopes), we can consider the fuller set of new syntactic elements.

public void Function(...)
{
  requires ...;
  ensures ...;
  assert ...;
  assume ...;
  ...
  if (...)
  {
    assert ...;
    assume ...;
    ...
  }
  else
  {
     assert ...;
     assume ...;
     ...
  }
  ...
}
@AdamSpeight2008
Contributor

@eyalsk Traits are independent of how they are validated. So not tied to a particular implementation. Eg it could use Code Contract method, Asserts, test harness.
Validated at compile time, or runtime or both. Its internal are in { } so can be collapsed, colorised (Imagine if a trait isn't satisfied having a red cross aside it.)
Premise the predicate is considered a requires, if not inside of a ensure block. Reducing the visual repeatition of Requires and Ensure)

public int Insert(T item, int index) 
trait
{
   // Anonymous trait
    range:  0 <= index  < Count;
   ensure { range:  0 <= return < Count;}
}

{
    return InsertCore(item, index);
}

Collapsed

public int Insert(T item, int index) 
trait [+]
{
    return InsertCore(item, index);
}
@eyalsk
eyalsk commented Aug 18, 2015

@AdamSpeight2008 Okay, thanks for the clarification, yeah this certainly works but it bothers me that it's designed to do more than just contracts.

I don't like regions at all so I don't really like the fact that you can collapse it.

With CodeContracts I can do something like this.

[ContractClass(typeof(ITemplateIteratorContract))]
public interface ITemplateIterator
{
    char Current { get; }

    int Index { get; }

    int Length { get; }

    void Back();

    bool Next(int steps = 1);
}

[ContractClassFor(typeof(ITemplateIterator))]
internal abstract class ITemplateIteratorContract : ITemplateIterator
{
    public abstract char Current { get; }

    public int Index
    {
        get
        {
            Contract.Ensures(Contract.Result<int>() >= -1);

            return default(int);
        }
    }

    public int Length
    {
        get
        {
            Contract.Ensures(Contract.Result<int>() >= 0);

            return default(int);
        }
    }

    public abstract void Back();

    public bool Next(int steps = 1)
    {
        Contract.Requires(steps > 0);

        return default(bool);
    }
} 

This allows me to keep the code clean especially in places where I have many contracts, I kinda like this approach but it can be a lot more terse and powerful with language support.

@Lexcess
Lexcess commented Aug 19, 2015

@eyalsk I agree I'd want to see a robust implementation of the current contract feature set before looking to extend it to scope level. There is some value there but not as much as with the application to class members (where it helps document and constrain the API).

I must admit I was never a fan of the ContractClassFor construct, obviously there are limited options when dealing with Interfaces and without drastic (and unlikely) language support changes something like this will likely be the implementation going forward.

I could see a Trait style class being a backing model for this kind of thing (and make reuse/meta data reading easier), but it would seem to imply that the existing contracts namespace would be essentially abandoned. I'm not necessarily against that, but it implies a lot more work.

@AdamSobieski

Language Support for Program Analysis

Language support for static and runtime program analysis could be the next LINQ and could expand to include some logic programming topics (#119 (comment) § Code Contracts Engine API and Logic Programming) (#119 (comment) § Scopes) (#119 (comment) § Scopes).

Syntax

Postfix Attributes

Some of you interested in metadata might enjoy the idea of postfix attributes. Runtime serializable expressions, expression trees, could also be constructor parameters of attributes. In theory, the postfix attributes, with expressions for constructor parameters, could reference the syntactic elements of interfaces, classes, fields, properties, methods or parameters.

@eyalsk
eyalsk commented Aug 19, 2015

@Lexcess Yeah ContractClassFor is kinda ugly. :)

@AdamSobieski How does your suggestion relate to this proposal? :)

@AdamSobieski

Virtual Methods on Interfaces

Virtual methods on interfaces (#119 (comment) § Virtual Methods on Interfaces) (#119 (comment) § Syntax § Interfaces) (#258) (#73) could also be of use for traits (#129) (#73) and mixins (#73).

@eyalsk
eyalsk commented Aug 20, 2015

@AdamSobieski I got you, thanks.

@AdamSobieski

Discussion

@stephentoub presented:

public int Insert(T item, int index)
    requires index >= 0 && index <= Count
    ensures return >= 0 && return < Count
{
    return InsertCore(item, index);
}

@AdamSpeight2008 presented (#129):

public int Insert(T item, int index) 
trait
{
   // Anonymous trait
    range:  0 <= index  < Count;
   ensure { range:  0 <= return < Count;}
}

{
    return InsertCore(item, index);
}

Syntax

Method Delegation

public class List<T> : IList<T>
{
  ...

  public int Insert(T item, int index)
    : <<{1}>>, <<{2}>>
  {
    return InsertCore(item, index);
  }

  ...
}

Optimizations

Such a syntax can facilitate compiler or runtime optimizations beyond:

public class List<T> : IList<T>
{
  ...

  public int Insert(T item, int index)
  {
    <<{1}>>;
    <<{2}>>;
    return InsertCore(item, index);
  }

  ...
}

Example 1

public class List<T> : IList<T>
{
  ...

  int Function(T item, int index)
  {
    requires 0 <= index & index < this.Count;
    ensures 0 <= return & return < this.Count;
    return default(int);
  }

  public int Insert(T item, int index)
    : Function(item, index)
  {
    return InsertCore(item, index);
  }

  ...
}

Example 2

public class List<T> : IList<T>
{
  ...

  int Function1(T item, int index)
    : Function3(item, index), Function4(item, index)
  {
    ...
  }

  int Function2(T item, int index)
    : Function5(item, index), Function6(item, index)
  {
    ...
  }

  int Function3(T item, int index)
  {
    ...
  }

  int Function4(T item, int index)
  {
    ...
  }

  int Function5(T item, int index)
  {
    ...
  }

  int Function6(T item, int index)
  {
    ...
  }

  public int Insert(T item, int index)
    : Function1(item, index), Function2(item, index)
  {
    return InsertCore(item, index);
  }

  ...
}

Inheritance Example 1

public class List<T> : BaseClassList<T>, IList<T>
{
  ...

  public int Insert(T item, int index)
    : base.Function(item, index)
  {
    return InsertCore(item, index);
  }

  ...
}

Type-casting Example

public class List<T> : IList<T>, IInterface<T>
{
  int IInterface<T>.Function(T item, int index)
  {
    requires 0 <= index & index < this.Count;
    ensures 0 <= return & return < this.Count;
    return default(int);
  }

  ...

  public int Insert(T item, int index)
    : ((IInterface<T>)this).Function(item, index)
  {
    return InsertCore(item, index);
  }

  ...
}

Type-casting Inheritance Example

public class List<T> : BaseClassList<T>, IList<T>, IInterface<T>
{

  ...

  public int Insert(T item, int index)
    : ((IInterface<T>)base).Function(item, index)
  {
    return InsertCore(item, index);
  }

  ...
}

Static Method Examples

Static Method Syntax

public static class Extensions
{
  public static int Function<T>(this IList<T> list, T item, int index)
  {
    requires 0 <= index & index < list.Count;
    ensures 0 <= return & return < list.Count;
    return default(int);
  }
}

public class List<T> : IList<T>
{
  ...

  public int Insert(T item, int index)
    : Extensions.Function<T>(this, item, index)
  {
    return InsertCore(item, index);
  }

  ...
}

Extension Method Syntax

public static class Extensions
{
  public static int Function<T>(this IList<T> list, T item, int index)
  {
    requires 0 <= index & index < list.Count;
    ensures 0 <= return & return < list.Count;
    return default(int);
  }
}

public class List<T> : IList<T>
{
  ...

  public int Insert(T item, int index)
    : this.Function<T>(item, index)
  {
    return InsertCore(item, index);
  }

  ...
}

Inheritance Extension Method Syntax

public static class Extensions
{
  public static int Function<T>(this IList<T> list, T item, int index)
  {
    requires 0 <= index & index < list.Count;
    ensures 0 <= return & return < list.Count;
    return default(int);
  }
}

public class List<T> : BaseClassList<T>, IList<T>
{
  ...

  public int Insert(T item, int index)
    : base.Function<T>(item, index)
  {
    return InsertCore(item, index);
  }

  ...
}

Type-casting Method Syntax

public static class Extensions
{
  public static int Function<T>(this IList<T> list, T item, int index)
  {
    requires 0 <= index & index < list.Count;
    ensures 0 <= return & return < list.Count;
    return default(int);
  }
}

public class List<T> : IList<T>
{
  ...

  public int Insert(T item, int index)
    : Extensions.Function<T>((IList<T>)this, item, index)
  {
    return InsertCore(item, index);
  }

  ...
}

Type-casting Extension Method Syntax

public static class Extensions
{
  public static int Function<T>(this IList<T> list, T item, int index)
  {
    requires 0 <= index & index < list.Count;
    ensures 0 <= return & return < list.Count;
    return default(int);
  }
}

public class List<T> : IList<T>
{
  ...

  public int Insert(T item, int index)
    : ((IList<T>)this).Function<T>(item, index)
  {
    return InsertCore(item, index);
  }

  ...
}

Lambda Expressions

public class List<T> : IList<T>
{
  ...

  public int Insert(T item, int index)
    : ((IList<T> x1, int x2) =>
    {
      requires 0 <= x2 & x2 < x1.Count;
      ensures 0 <= return & return < x1.Count;
      return default(int);
    })(this, index)
  {
    return InsertCore(item, index);
  }

  ...
}

Interfaces, Virtual Interface Methods

[...]
public interface Trait<T>
{
  [...]
  int Function(T item, int index)
  {
    requires 0 <= index & index < ((IList<T>)this).Count;
    ensures 0 <= return & return < ((IList<T>)this).Count;
    return default(int);
  }
}

public class List<T> : IList<T>, Trait<T>
{
  ...

  public override int Insert(T item, int index)
    : ((Trait<T>)this).Function(item, index)
  {
    return InsertCore(item, index);
  }

  ...
}

Inheritance Example 2

public interface IList<T>
{
  ...

  int Insert(T item, int index)
  {
    requires 0 <= index & index < this.Count;
    ensures 0 <= return & return < this.Count;
    return default(int);
  }

  ...
}

public class List<T> : IList<T>
{
  ...

  /* See also: http://research.microsoft.com/en-us/projects/contracts/userdoc.pdf, Chapter 3 */
  public override int Insert(T item, int index)
    : base(IList<T>).Insert(item, index)
  {
    return InsertCore(item, index);
  }

  ...
}

Inheritance Example 3

public interface IList1<T>
{
  ...

  int Insert(T item, int index)
  {
    requires 0 <= index & index < this.Count;
    return default(int);
  }

  ...
}

public interface IList2<T>
{
  ...

  int Insert(T item, int index)
  {
    ensures 0 <= return & return < this.Count;
    return default(int);
  }

  ...
}


public class List<T> : IList1<T>, IList2<T>
{
  ...

  /* See also: http://research.microsoft.com/en-us/projects/contracts/userdoc.pdf, Chapter 3 */
  public override int Insert(T item, int index)
    : base(IList1<T>).Insert(item, index), base(IList2<T>).Insert(item, index)
  {
    return InsertCore(item, index);
  }

  ...
}

Notes

Reusability

The syntax presented by @stephentoub, with a different style of static methods and processing, can facilitate reusability:

public static class Extensions
{
  public static bool Range<T>(IList<T> list, int x)
  {
    return 0 <= x & x < list.Count;
  }
}

public class List<T> : IList<T>
{
  ...

  public int Insert(T item, int index)
    requires Extensions.Range(this, index)
    ensures Extensions.Range(this, return)
  {
    ...
  }

  ...
}

Method delegation with calling static target methods facilitates reusability.

public void Function(string x1, int x2)
  : Extensions.NotNull(x1), Extensions.Range(this, x2)
{
  ...
}

Scoping

The syntax can provide expressiveness to refer to a delegating or calling method.

A return keyword would seem to require either inline method expansion semantics on the target methods (otherwise a return keyword would be regarding the target methods as opposed to the delegating or calling methods) or new syntax for referring to delegating or calling methods from target methods including utilizing new functionality, e.g. System.Diagnostics.Contracts.Contract.Result<T>(/*...*/).

Parametric Polymorphism

Method delegation could expand parametric polymorphism, expanding the expressiveness of methods, including the method MakeGenericType.

public override Type MakeGenericType(params Type[] typeArguments)
  : Extensions.ArrayCount(typeArguments, ...), ...
{
  ...
}

See also templates:

public override Type MakeGenericType(params object[] arguments)
  : Extensions.ArrayCount(arguments, ...), ...
{
  ...
}
@bbarry
bbarry commented Aug 24, 2015

nit:

ensures 0 <= return & return < this.Count;

you are using bitwise & over and over in these, I'm assuming you mean logical && instead...

@AdamSobieski

@bbarry , thanks for the comment.

The & is a logical as well as bitwise operator. & is described as evaluating both operands regardless of the first operand's value. && evaluates utilizing short-circuit evaluation where the second operand is not evaluated if the first is false. X1 && X2 is equivalent to if(X1) { return X2; } else { return false; }.

To the matter of ensures semantics: ensures X1 && X2, ensures X1 & X2 or ensures X1, X2. In the notes about reusability, the example with the syntax presented by @stephentoub , we see that the compiler or runtime could obtain expression trees from functions such as Extensions.Range.

Envision & and && and various possible compiler optimizations, including runtime compiler optimizations, of large logical expression trees including those from scenarios resembling example 2 (see also: #119 (comment) § Lambda Expressions and Sets). In such optimization processes, separating the clauses is convenienced by & or semantics. The topics also pertain to the semantics of collections of expressions, sets or sequences.

@TChatzigiannakis

Reading the current discussion, there is one thing that's unclear to me:

Will it be valid to strengthen postconditions (by adding more ensures clauses) or weaken preconditions (by omitting/retracting some requires clauses) when specializing a virtual method?

@AdamSobieski

@TChatzigiannakis , thanks for the question. Your question is applicable to each syntax.

“If a subclass overrides the behavior of a method of a base class (or implements and interface), the preconditions it specifies can only be equivalent to or weaker than the base class.” [2]

“If a subclass overrides the behavior of a method of a base class (or implements and interface), the postconditions it specifies can only be equivalent to or stronger than the base class.” [2]

[1] Code Contracts User Manual
[2] Code Contracts and Inheritance

@AdamSobieski

Logic Programming

There are exciting possibilities pertaining to interoperability between runtime and static program analysis and logic programming (#119 (comment) § Code Contracts Engine API and Logic Programming).

The following example indicates the method delegation syntax and static methods with a context-related or logic engine parameter.

public static class LogicProgramming
{
  public static bool Function1(this LogicEngineContext engine, Variable x1, Variable x2)
    : Extensions.Extension1(engine, x1), Extensions.Extension2(engine, x2)
  {
    ...
  }

  public static bool Function2(this LogicEngineContext engine, Variable x1, Variable x2)
    : LogicProgramming.Function1(engine, x1, x2), ...
  {
    ...
  }
}
@kruncher

I rather like this idea; although I feel that it would be nice if the syntax highlighting of C# made the contracts appear much fainter so that they don't "overpower" the method signature/body when glancing through code.

By fainter I mean, the way in which unused namespaces are sort of ghosted.

But yeah; a big +1 for this feature :)

@dsaf
dsaf commented Oct 22, 2015

@kruncher Either that or maybe introduce a generally more granular syntax coloring configuration.

@asvishnyakov
Contributor

We need the support of at least runtime enforced contracts (simply turn into checks throwing exceptions) to clean shit (Contract.Requires, Contract.Enruses) from our code. requires also will be better replacement for if (...) { throw ... } construction which used in most projects. So, I think, Roslyn team should give high priority to (simple) contracts support in C# 7. Also, I think, it's Code Contracts team must change their tools to support this language feature, not language feature must be backward-compatible with existing Code Contracts tools.

@gafter gafter added 2 - Ready and removed 1 - Planning labels Nov 20, 2015
@GregReddick GregReddick referenced this issue in DotNetAnalyzers/StyleCopAnalyzers Nov 20, 2015
Closed

Rule proposal: Layout rule - Blank line required after contract #759

@gafter gafter modified the milestone: C# 7 and VB 15 Nov 21, 2015
@Daniel-Svensson

I've made a simple proof of concept implementations which allows part of the proposed syntax to be tried out. It is availible at https://github.com/Daniel-Svensson/roslyn
Currently I have no plans to take this further, but feel free to use it as a base for further exploration of the syntax.

The concept implementation is quite basic and it is not a complete implementation or a specification how I think it should be. For example

  • It does not allows "return" to be used in ensures, you must use Contract.Result() instead
  • It relies on the code contract rewriter to implement logic such as what is a valid pre/post condition etc.
  • It requires code contracts to be used in all projects using the syntax or the Contract calls will be optimised away
  • It does not allow you to throw exceptions (since the implementation relies on code contracts you would only partially be able to specify the exception arguments
  • It does not allows a distinction between "release" and "debug" contracts (such as the proposed "assert")

The concept allows you to use "requires expression" and "ensures expression" syntax between the method declarations and it's implementation. The syntax is translated to simple calls to Contract.Requires(expression) and `Contract.Ensures(expression).

Example:

The following code

 private static void Test(int a)
    requires a >= 0
    requires a <= 10
 {
            Console.WriteLine(a.ToString());
 }

will be translated to

 private static void Test(int a)
 {
            Contract.Requires(a >= 0);
            Contract.Requires(a <= 10);
            Console.WriteLine(a.ToString());
 }

I am thinking about changing how the rewritten code looks like in order to support throwing exception or supporting "result" in ensures but I have not decided if I will do that or not.

@ghost
ghost commented Feb 7, 2016

@Daniel-Svensson thank you! it's definitely a start even though it is just a proof of concept, good job! :)

I'm really looking forward for this feature to be implemented into C# 7.

@alrz
Contributor
alrz commented Feb 7, 2016

If I write something like this,

void Test(string a) requires Regex.IsMatch(a, "...")  { ... }

Do I get a compile-time error if I pass an string literal that doesn't match?

And also, do I need to repeat the requires clause for each of these methods,

void F(int a) requires a > 0 { ... }
void G(int b) { F(b); }

or G would infer them by usage?

@omariom
omariom commented Feb 8, 2016

Joe Duffy talks about Midori's team experience of designing and using contracts.

http://joeduffyblog.com/2016/02/07/the-error-model/

@AdamSpeight2008
Contributor

@omariom I was just going to mention this. as it seems a like a lot of what people are wanting is / was implemented within this C# variant (Midori)

@eyalsk
eyalsk commented Feb 9, 2016

@omariom Great read! thanks.

@eyalsk
eyalsk commented Jun 8, 2016

@jaredpar is there anything new in this space? will we ever see this as part of the language? I know it won't be part of C# 7 but is there any progress in this area? like discussions? different way to tackle this? what are your thoughts?

@gulshan
gulshan commented Aug 16, 2016
  • I think, contracts should follow same resolution as nullability checking. Nullability checking for non-nulable classes can change a bit (I think I should mention @gafter here)-
    • Warnings should be shown in compile-time, if compiler cannot determine whether the caller is fully complied by the contracts through static analysis. Some if condition checking in the caller's side should be enough to satisfy the compiler static analysis and remove warnings in this case.
    • If static analysis can determine a sure contract violation (for example in the cases of literals or constant values), errors should be shown at compile-time error.
    • Otherwise ok. 😄
  • Invariants should be implemented for records, if not for all the classes. As records are immutable, an invariant is just contacts attached to the constructor.
@mcetkovsky

Mads Torgersen - MSFT at https://blogs.msdn.microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/

Nothing on design by contract this time around, and I’m not sure if there will be anytime soon. We’ve looked a lot at contract-style features, and they tend to get quite verbose and complicated. I am not sure that they would carry their weight in added value as language features. But maybe, one day, if we can get the design right.
While people do use CodeContracts for many things, the 90% case seems to be dealing with null. We are eager to tackle the language integration of that in a more type-system-oriented way, allowing you to distinguish between nullable and nonnullable reference types and track correct usage based on that. I hope this can happen in the next major release of C#.

@eyalsk
eyalsk commented Aug 27, 2016

@mcetkovsky Thank you for that, range checks are also valuable so it's not only null checks but they are right in that DbC tend to be verbose and complicate things.

Maybe someone can narrow the use-cases and come up with a better solution.

@asvishnyakov
Contributor

We just need extension support by compiler. Then Code Contracts team will apply language extension step-by-step and after their work will complete, Roslyn team will may start discussion about including that code to compiler.

@SunnyWar

I lend my I support the final solution Midori came up with:

Joe Duffy said about contracts in Midori:

We ended up with a single kind of contract: one that was part of an API’s signature and checked all the time. If a compiler could prove the contract was satisfied at compile-time – something ... it was free to elide the check altogether. But code was guaranteed it would never execute if its preconditions weren’t satisfied. For cases where you wanted conditional checks, you always had the assertion system...

@jamesqo
Contributor
jamesqo commented Oct 8, 2016

Perhaps we could introduce a syntax for the developer to indicate that he/she wants the checks to be debug-only? For example, prefixing with @:

int NextPowerOfTwo(int value) @requires value < 0
{
}

This particular example would kind of line up with how you prefix commands with @ in Batch scripting and Makefiles to 'quiet' the normal echo you get from the command. (Although C# is a very different language from both of those.)

Alternatively, we could introduce an attribute such as [DebugOnlyContract], but that would take away much of the convenience of using this feature for debug checks I guess.

@kruncher
kruncher commented Oct 8, 2016

I would personally prefer the more verbose (but readable!) attribute over the obscure syntax.

@jamesqo
Contributor
jamesqo commented Oct 8, 2016

@kruncher The meaning would be clear after the first time you learn what it means, so that would only be an issue for newcomers.

I guess another option though would be to simply have a debug contextual keyword like this:

int NextPowerOfTwo(int value) debug requires value > 0
{
}

Would be less typing than adding an attribute, and admittedly more readable than the @ syntax for newcomers, but it would require an extra few keystrokes.

@kruncher
kruncher commented Oct 8, 2016

What is the advantage in having a debug only contract; surely a contract is a contract?

@jamesqo
Contributor
jamesqo commented Oct 8, 2016

@kruncher We may not want extra checks we know are true in performance-sensitive code. For example, it may prevent the JIT compiler from inlining a particular method. For example if you go to corefx where the BCL lives, you'll see that basically all of the checks of the internal/private methods are Debug.Asserts.

@kruncher
kruncher commented Oct 8, 2016

Hmmm, would it be useful to have a build option to simply disable all contracts for internal/private methods (i.e. an option that can be configured on/off for 'Debug', 'Release' or 'Custom')?

@jamesqo
Contributor
jamesqo commented Oct 8, 2016

@kruncher Hmm... might be a good idea, actually. A problem though is that you might want to enable the contracts for only certain internal/private methods.

@Lexcess
Lexcess commented Oct 10, 2016

In the Code Contracts implementation there is a explicit option for only checking 'public surface contracts' which exhibits this behaviour. The clear win here is that if a contract is asserted on private methods the Code Contracts implementation will require another assertion or assumption it on any callee meaning that if all callees have already validated the input you don't take the perf hit on checking again.

I'd say this behaviour is the same (or a slight subset of) the statement: "If a compiler could prove the contract was satisfied at compile-time – something ... it was free to elide the check altogether" from earlier in this thread.

@jamesqo
Contributor
jamesqo commented Oct 10, 2016

@Lexcess

meaning that if all callees have already validated the input you don't take the perf hit on checking again.

Would not always work. For example, consider this code sample:

T Single<T>(IList<T> list)
{
    if (list == null || list.Count != 1) throw something;

    return SingleInternal(list);
}

T SingleInternal<T>(IList<T> list)
{
    Debug.Assert(list != null && list.Count == 1);

    return list[0];
}

A bad implementation of IList<T> could return different values of Count for the 2 invocations, even though it was not actually modified. So the compiler could not prove that the list.Count == 1 constraint would be satisfied, and would have to check the Count twice.

@Lexcess
Lexcess commented Oct 10, 2016 edited

I see what you are saying but I think it is a separate point. I think the point that you could have untrustworthy implementations (note that the MS Code Contracts has contracts for interfaces like IList so you could solve this) is different from needlessly reapplying the same checks all the way down a call stack.

It'd be interesting to see what the Code Contracts does around non-predictable return types such as your example and arbitrary yield returns. I've often found it's analyser was quite good at unravelling such problems.

@aarondandy

For that example Code Contracts seems to take the "if it hurts don't do that strategy" and assumes you are not trying to intentionally harm the stability of your system. It does this by requiring methods used in a contract to be [Pure] but it does not enforce that the method or property is actually "Pure".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment