Skip to content

Add strict mechanism for opting into stricter subsets of the language  #54903

@Keno

Description

@Keno

We've had a few discussions the past few weeks about a feature tentatively dubbed pragma strict after similar constructs in other languages. However, there wasn't really a cohesive writeup of the intent, so triage asked me to write one up to serve as the basis for discussion and fleshing out. I intend to edit this issue as the idea evolves.

Basic idea

The basic idea of the pragma strict feature is to have an opt-in mechanism of turning julia programs that are semantically valid, but undesirable for other reasons (e.g. using ambiguous syntax that should have arguably been disallowed, but we can't for backwards compatibility reasons) into errors. This would be an opt-in feature for developers who have personal, organizational or regulatory requirements for requiring stricter coding standards. An additional motivation is to provide an additional vehicle for low-frictition language evolution. For example, if a specific opt-in turns out to be popular across the majority of packages, a potential julia 2.0 that made the opt-in automatic while technically breaking, would be largely non-breaking in practice.

We are not imagining a single strict mode opt in here, but rather a finer grained set of options, plus versioned collections of options for particular use cases. See the last section for a an initial list of such options.

It is worth emphasizing again that this feature is only intended to disallow undesirable programs that are otherwise semantically valid. It is not intended to cause meaningful semantic differences in programs that are valid both in standard semantics and under the opt-in restrictions (i.e. turning on the restrictions may cause things to error, but if they don't the program should behave the same).

How does the opt-in work?

One of the primary questions in this proposal is how the user expresses the opt-in. There's a few separate semantic options, each
with a number of potential syntax options.

  1. Per module opt-in like our existing Experimental.@compiler_options
  2. Per file opt-in (e.g. using a magic comment on the first line) - popular in some other languages
  3. Per project opt-in in Project.toml

After some discussion on triage, a Project.toml-level opt-in seems like the best option. The primary motivation here is to allow opt-ins that need to be done in the parser (e.g. whitespace requirements). We don't currently define the execution ordering of parsing and execution for packages, so a module-toplevel opt-in may be semantically too late (relatedly, it may be ambiguous what happens when the opt-in is placed in the middle of a disallowed parse). An additional concern is that ideally IDE tooling would be able to understand the active set of restrictions without having to look at the code.

Concrete Project.toml syntax options

One convenient option would be reusing Preferences.jl. One might imagine a julia-level preference like:

name = "MyPackage"

[preferences.julia]
strict = ["nomultiassign", "uniqueidentifiers"]

This doesn't fully mesh with the usual preferences semantics, since preferences are ordinarily uniqued per-UUID while
they would be private for a particular package, but this might be ok. Alternatively, we could reserve the strict key
in each individual package's preference table:

name = "MyPackage"

[preferences.MyPackage]
strict = ["nomultiassign", "nolocalshadow", "noglobalshadow"]

Alternatively, we could have a new top-level strict section:

name = "MyPackage"
[strict]
julia = ["nomultiassign", "nolocalshadow", "noglobalshadow"]

Initial idea list for opt-in options

In this section, I'm collecting a list of potential options that might be implemented. However, I am not at this point asking people to brainstorm all the possibilities that could be implemented. I'm also not asking for detailed discussion on what should or should not be included in a particular option. Rather, I wanted to have a place to list all the ideas that have already come
up and a place to link any issues that could be addressed by this feature. Full design discussions for individual flags can be had on the PRs to implement them once the overall mechanism is in place.

Individual options

  • nomultiassign

Disallows multiple assignments in the same expression without parantheses. I.e. disallows a = b, c = d, e, = f = (1, 2)

  • nolocalshadow

Disallows shadowing of local variables, e.g. in the following


function foo()

	for i = 1:10

		for i = 1:10 # Error shadowing local `i` 

		end

		all(1:10) do i # Error shadowing local `i`
			iszero(i)
		end
	end
end
  • noglobalshadow

Disallows shadowing of global variables, e.g. in the following:

function foo()
	missing = false # Error local `missing` shadows imported global `missing`
end
  • Some variant of unique assignment

Stefan had proposed introducing a unique assignment operator, e.g. := for which there would then be a corresponding opt-in to enforce all assignments use it

  • Enforce export versioning

If we implement some variant of export versioning, there could be an opt-in forbidding unversioned exports.

Collections

The idea of collections is that users in general don't want to individually decide which opt ins matter to them, but will likely be following a standard set by their organizations or prescribed by a style guide. To this end, there could be meta opt-ins like "basestyle", which would
activate a standard collection of opt-ins. These collections should be versioned and activated based on the min-compat version of Julia. In this way, new opt-ins can be added to a collection, without automatically activating them on a julia version upgrade.

Metadata

Metadata

Assignees

No one assigned

    Labels

    designDesign of APIs or of the language itselffeatureIndicates new feature / enhancement requests

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions