Compartilhar via


Software Contracts, Part 7: Contracts as annotations - language features used to express contracts

My last post on contracts introduced the idea that a languages type system can be used as a mechanism to expose the contract of a function.  It turns out that language designers started recognising this fact and they started adding annotations to types that allow the contract for functions to be expressed.

As is to be expected, originally these annotations were relatively limited in scope - for instance, the C language added a concept of "type qualifier" when it was standardized (K&R had no such concept).  They only added a couple of simple qualifiers - const and volatile.  When a parameter to a function was declared to be "const", it meant that the function's contract included a guarantee not to modify the parameter [1]. 

Of course, sometimes it was rather hard to decypher the difference between "const char *" and "char *const" (the first is a pointer to a constant character, the second is a constant pointer to a character), but the intent was there.  The C language specification treated the type qualifier mostly as a hint - it didn't do a particularly good job of enforcing them.

 

For C++, the language designers went further than in C. They tightened up the semantics for "const" adding stricter type checks.  And they added new parameter type, a "reference" parameter.  When a function took a reference to a variable as a parameter, it essentially says "the contract for this function includes the ability to modify the contents of this variable".

 

 

Next:  Contract Annotations enforced outside the compiler.

[1] Yeah, I know about CreateProcess.

Edit: Ok, I screwed up the definitions of const char * and char * const.

Comments

  • Anonymous
    January 24, 2007
    I think that "const char *" means a pointer to a constant character, and "char *const" means a constant pointer to a character, just the opposite of what you said.

  • Anonymous
    January 24, 2007
    "Of course, sometimes it was rather hard to decypher the difference between "const char *" and "char *const" (the first is a constant pointer to a character, the second is a pointer to a constant character), but the intent was there." I believe you've gotten those backwards.  A "const char *" is equivalent to a "char const *", which is a pointer to constant characters -- ie. you can change which set of characters the pointer points to but you can't change the characters themselves.  Whereas a "char * const" is a constant pointer to non-constant characters, meaning that you can change the characters as much as you like but you can't make the pointer point at a different set of them.

  • Anonymous
    January 24, 2007
    const char* is a pointer to a constant character.  char* const is a constant pointer to a character.  I think your description got it backwards.  I guess that kind of proves your point, though...

  • Anonymous
    January 24, 2007
    > When a parameter to a function was declared to be "const", > it meant that the function's contract included a guarantee That was more or less the intent but not the actual meaning.  See below. > The C language specification treated the type qualifier mostly > as a hint - it didn't do a particularly good job of enforcing > them. The C language specification essentially doesn't enforce anything.  In some cases it requires that at least one diagnostic be emitted during translation, but it doesn't require that diagnostic to be distinguishable from one that could be emitted while properly translating a perfectly valid program.  It never requires translation to abort -- an implementation is always allowed to finish translating and executing (in some way or other) a program even if it has issued a warning. What the C language specification essentially does do is entirely avoid defining any meaning for programs that violate various conditions of the standard.  This is the infamous "undefined behaviour".  A perfect example is modification of an object which was defined as const.  Such modification can be attempted by casting a const pointer to non-const and assigning to the resulting lvalue, for which the C standard says that the C standard says nothing at all about the effect of the entire program.  Undefined behaviour.  No guarantees. If you want enforcement, C isn't the place to get it.

  • Anonymous
    January 24, 2007
    > Of course, sometimes it was rather hard to decypher the difference > between "const char *" and "char *const" (the first is a constant > pointer to a character, the second is a pointer to a constant > character), but the intent was there. It's actually the other way around, const char * is a pointer to a constant character and char * const is a constant pointer to a character.   I find it easier just to write them both in the same manner to avoid that kind of confusion, so char const * and char * const, then you can just read it from right to left.

  • Anonymous
    January 24, 2007
    I'm sure you meant that const char* means pointer to a constant character. The idea is that the const keyword is written before the thing that is constant...

  • Anonymous
    January 25, 2007
    One of the most-useful annotations is just starting to come into the mainstream languages: the "notnull" property (of a pointer). C++ "References" are implicitly "not null", i.e. a reference is guaranteed to refer to "something" (except by casting? my memory is hazy), i.e. it is a non-nullable pointer. The introduction of annotations should be guided by bug frequency statistics: In malloc/free-languages, more than 50% of bugs tend to be allocation-related; in managed-memory languages (gc/refcount), most bugs tend to be nullpointer dereferences. Of course, the hardest-to-find bugs are parallelism/synchronization bugs, so attributes like "is-reentrant" or "is-multithreaded" would be quite nice.

  • Anonymous
    January 25, 2007
    Software contracts seem like an important issue for programming in the large, so why isn't it more common? Pascal had the ability to restrict integer ranges with a declaration like 0..400, but I haven't seen that in other languages. Java tried to address the declaration of thrown exceptions although programmers generally hate it. Has C# made any progress in this area?

  • Anonymous
    January 25, 2007
    The comment has been removed

  • Anonymous
    January 25, 2007
    Modern fortran includes function-level qualifier "pure" meaning the function has no side effects.  As a result, the compiler can do them in any order (or in parallel). Pretty cool! Link: http://www.ncsa.uiuc.edu/UserInfo/Resources/Hardware/IBMp690/IBM/usr/share/man/info/en_US/xlf/html/lr82.HTM#HDRPURE

  • Anonymous
    January 25, 2007
    Ok, it's taken 7 other posts, but we've finally gotten close to where I wanted to be when I started this

  • Anonymous
    January 25, 2007
    The comment has been removed

  • Anonymous
    January 26, 2007
    The comment has been removed

  • Anonymous
    January 29, 2007
    > This is a circular argument. As long as mainstream languages and > development tools don't support embedded contract terms, it will > be cumbersome and expensive. Do you really think so?   I'd propose that it isn't the act of dictating/maintaining a known contract that's really cumbersome.  It's determining what that contract is in the first place.  I've found it to be an activity that's pretty front-heavy, and I don't have to be my manager to know that it's hard to get a budget and schedule that's properly weighted for design when all that money's being dumped into the COPQ portion of the budget to fix problems in the field.   (Because the last set of products wasn't really designed either.)