Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation of nameonly and default values for formals #3903

Merged
merged 10 commits into from
May 17, 2023
44 changes: 17 additions & 27 deletions docs/DafnyRef/Expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -1825,6 +1825,21 @@ Method calls, object-allocation calls (`new`), function calls, and
datatype constructors can be called with both positional arguments
and named arguments.

Formal parameters have three ways to indicate how they are to be passed in:
- nameonly: the only way to give a specific argument value is to name the parameter
- positional only: these are nameless parameters (which are allowed only for datatype constructor parameters)
- either positional or by name: this is the most common parameter

A parameter is either required or optional:
- required: a caller has to supply an argument
- optional: the parameter has a default value that is used if a caller omits passing a specific argument

The syntax for giving a positional-only (i.e., nameless) parameter does not allow a default-value expression, so a positional-only parameter is always required.

At a call site, positional arguments are not allowed to follow named arguments. Therefore, if `x` is a nameonly parameter, then there is no way to supply the parameters after `x` by position.
Thus, any parameter that follows `x` must either by passed by name or have a default value.
That is, if a later (in the formal parameter declaration) parameter does not have a default value, it is effectively nameonly.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this paragraph explains "effectively nameonly", you may want to add a similar paragraph that explains "effectively required" (see #3864).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I found the 'effectively' terminology unhelpful -- better is simply stating and enforcing the rules about which formals can be optional and which can be nameonly.


Positional arguments must be given before any named arguments.
Positional arguments are passed to the formals in the corresponding
position. Named arguments are passed to the formal of the given
Expand All @@ -1837,32 +1852,7 @@ value for each optional parameter, and must never name
non-existent formals. Any optional parameter that is not given a value
takes on the default value declared in the callee for that optional parameter.

## 9.37. Formal Parameters and Default-Value Expressions

The formal parameters of a method, constructor in a class, iterator,
function, or datatype constructor can be declared with an expression
denoting a _default value_. This makes the parameter _optional_,
as opposed to _required_. All required parameters must be declared
before any optional parameters. All nameless parameters in a datatype
constructor must be declared before any `nameonly` parameters.

The default-value expression for a parameter is allowed to mention the
other parameters, including `this` (for instance methods and instance
functions), but not the implicit `_k` parameter in least and greatest
predicates and lemmas. The default value of a parameter may mention
both preceding and subsequent parameters, but there may not be any
dependent cycle between the parameters and their default-value
expressions.

The well-formedness of default-value expressions is checked independent
of the precondition of the enclosing declaration. For a function, the
parameter default-value expressions may only read what the function's
`reads` clause allows. For a datatype constructor, parameter default-value
expressions may not read anything. A default-value expression may not be
involved in any recursive or mutually recursive calls with the enclosing
declaration.

## 9.38. Compile-Time Constants {#sec-compile-time-constants}
## 9.37. Compile-Time Constants {#sec-compile-time-constants}

In certain situations in Dafny it is helpful to know what the value of a
constant is during program analysis, before verification or execution takes
Expand Down Expand Up @@ -1903,7 +1893,7 @@ In Dafny, the following expressions are compile-time constants[^CTC], recursivel

[^CTC]: This set of operations that are constant-folded may be enlarged in future versions of `dafny`.

## 9.39. List of specification expressions {#sec-list-of-specification-expressions}
## 9.38. List of specification expressions {#sec-list-of-specification-expressions}

The following is a list of expressions that can only appear in specification contexts or in ghost blocks.

Expand Down
63 changes: 62 additions & 1 deletion docs/DafnyRef/Types.md
Original file line number Diff line number Diff line change
Expand Up @@ -1886,7 +1886,7 @@ Furthermore, for the compiler to be able to make an appropriate choice of
representation, the constants in the defining expression as shown above must be
known constants at compile-time. They need not be numeric literals; combinations
of basic operations and symbolic constants are also allowed as described
in [Section 9.38](#sec-compile-time-constants).
in [Section 9.37](#sec-compile-time-constants).

### 5.7.1. Conversion operations {#sec-conversion}

Expand Down Expand Up @@ -4502,4 +4502,65 @@ imposed on the body of `ReachableVia` makes sure that, if the
predicate returns `true`, then every object reference in `p` is as old
as some object reference in another parameter to the predicate.

## 6.5. Nameonly Formal Parameters and Default-Value Expressions

A formal parametes of a method, constructor in a class, iterator,
function, or datatype constructor can be declared with an expression
denoting a _default value_. This makes the parameter _optional_,
as opposed to _required_. All required parameters must be declared
before any optional parameters.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is no longer true (once #3864 is merged). A required parameter is allowed to follow an optional parameter, provided the required parameter is nameonly. (It may be best to omit the sentence here and explain this below, after nameonly has been introduced.)

Actually, L4538 says what needs to be said.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the sentence.


For example,
<!-- %check-resolve %save f.tmp -->
```dafny
function f(x: int, y: int := 10): int
```
may be called as either
<!-- %check-resolve %use f.tmp -->
```dafny
const i := f(1,2);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add space after ,

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

const j := f(1);
```
where `f(1)` is equivalent to `f(1,10)` in this case.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add space after ,

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK


Formal parameters may also be declared `nameonly`, in which case a call site
must explicitly name the formal when providing its actual argument.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be nicer to explain nameonly after explaining (in lines 4522-2527) that actual parameters may be given by name.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


The above function may be called as
<!-- %no-check -->
```dafny
var k := f(y:=10, x:=2);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a space on both sides of :=

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

```
using names; here, though there are no `nameonly` parameters so the names may be used
or the actual arguments may be given in order.

If a function is declared with a `nameonly` formal, then that formal's value must be given with a named assignment.
For example, a function `ff` declared as
<!-- %check-resolve -->
```dafny
function ff(x: int, nameonly y: int): int
```
may be called only using `ff(0, y := 4)`. A `nameonly` formal may also have a default value and thus be optional.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"only" here feels a bit confusing. For one, the parameters don't have to be passed in as 0 and 4. But also, one can call ff with ff(y := 4, x := 0).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Fixed.


Any formals after a `nameonly` formal must either be `nameonly` themselves or have default values.
Otherwise an error will result when an attempt is made to call the function or method with such a formal list.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L4539 describes the motivation for L4538, from the language-design perspective. If L4538 is violated, Dafny will give an error for the declaration of the formals, so what happens at a call site is a moot point. I suggest keeping just L4538 and deleting L4539.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this code

function f(x: int, nameonly y: int, z: int): int

const c := f(0, y:=0)

Dafny gives an error on the const declaration, but not on the function.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently, this has been fixed since Dafny 4.0. We now get an error on the function declaration as well.


The formals of datatype constructors are not required to have names. Such formals may have default values but
may not be declared `nameonly`. Such nameless formals must be declared before `nameonly` formals.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, a nameless formal is neither allowed to have a default value nor allowed to be declared nameonly. Also, missing here is the rule that a nameless formal parameter is not allowed to follow an optional parameter. I suggest replacing the second and third sentences with "A nameless parameter is not allowed to follow a parameter with a default value.".

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, but I would think a default value for a nameless formal to be a perfectly reasonable thing.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. It would be a perfectly reasonable thing. We could add it in the future.


The default-value expression for a parameter is allowed to mention the
other parameters, including `this` (for instance methods and instance
functions), but not the implicit `_k` parameter in least and greatest
predicates and lemmas. The default value of a parameter may mention
both preceding and subsequent parameters, but there may not be any
dependent cycle between the parameters and their default-value
expressions.

The well-formedness of default-value expressions is checked independent
of the precondition of the enclosing declaration. For a function, the
parameter default-value expressions may only read what the function's
`reads` clause allows. For a datatype constructor, parameter default-value
expressions may not read anything. A default-value expression may not be
involved in any recursive or mutually recursive calls with the enclosing
declaration.