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

infix keyword is allowed where it has no effect, and has undocumented uses #17738

Open
Sporarum opened this issue May 31, 2023 · 3 comments
Open
Labels
area:parser area:reporting Error reporting including formatting, implicit suggestions, etc area:specification About the WIP Scala 3 Spec itype:bug Spree Suitable for a future Spree

Comments

@Sporarum
Copy link
Contributor

Sporarum commented May 31, 2023

Compiler version

Tested on 3.3.1-RC1 with -source:future, but unlikely to have changed

Current State

The reference page describing infix calls mentions two places where infix is allowed (without saying these are necessarily the only two):

trait MultiSet:
  infix def union(other: MultiSet): MultiSet

val s1, s2: MultiSet

s1 union s2

and

infix type or[X, Y]
val x: String or Int = ???

However, I found infix is also allowed in the following (doubles as example for later):

object Ext:
  infix object O:
    def apply(x: Char) = 5
  infix val v: (String => String) = s => s
  infix def m(x: Int) = ???
  infix class C(x: Int)
  infix given g(using x: Int): (String => String) = s => s * x

infix def toplevel(x: Any, y: Any) = ???

Notably, infix is not allowed on import and export

toplevel can never be called as infix, the intent was probably an infix extension method

I would have expected to be able to call for example v in an infix matter, this is not allowed,
all of the following throw "expression expected but end of statement found" (also the case if we remove the infixs):

val fail1 = Ext O
val fail2 = Ext v
val fail3 = Ext m
val fail4 = Ext C

given Int = 4
val fail5 = Ext g

If we add right-hand sides, we have the following:

val rhs1 = Ext O 'c' //warning: Alphanumeric method O is not declared infix; it should not be used as infix operator.
Instead, use method syntax .O(...) or backticked identifier `O`.
val rhs2 = Ext v "hello"
val rhs3 = Ext m 4
val rhs4 = Ext C 5 //warning: Alphanumeric method C is not declared infix; it should not be used as infix operator.
Instead, use method syntax .C(...) or backticked identifier `C`.

But neither O nor C are methods, and more importantly, they are both inline !

And finally if we remove infix from the definitions, we get this (with shortened warnings):

val ninfix1 = Ext O 'c' //warning: not infix
val ninfix2 = Ext v "hello"
val ninfix3 = Ext m 4 //warning: not infix
val ninfix4 = Ext C 5  //warning: not infix

given Int = 4
val fail6 = Ext g "hello"

As we can see, neither v nor g emit a warning !
(This might be because Function1.apply was compiled with Scala 2)

Adding infix to O.apply leads to no warnings, whether or not O is itself infix!

The reference is also silent on types taking any other amount than 2 parameters, here is what I found:

  • 0:
type Test = [A, B] =>> Char
type Res = Int Test String
  • 1:
object Out:
  infix type F[A] = A
type Res = Out F Int //error: Not Found type Out //error: Not Found type F
  • 3 or more:
type Test[A, B, C]
type Res1 = Int Test String Char //error: Char does not take type parameters
type Res2 = Int Test [String, Char] //error: identifier expected but [ found

The following is also undocumented:

object Out:
  class Student(name: String, govtId: Int):
    infix def this(name: String) =
      this(name, 0)

val p = Out Student "James"

Summary

infix is valid, and has undocumented positive effect on traits and classes taking exactly 2 type parameters, and types taking 0 parameters.

infix is valid, but has no effect on val, object, given, as well as in the following particular cases:

  • type not taking 0 or 2 parameters
  • class or trait not taking 2 parameter
  • a toplevel def
  • a def taking 0 parameters (behaves the same as v above)

Expectation

infix should only be allowed in front of constructs it can modify. Currently: def, type, trait, class
In other cases, it should return a similar error as infix export, or a better one

In cases where infix has no effect (currently: "particular cases" above), a warning should be emitted

Every ninfixN should throw a "not infix" warning

The following should display a special warning:

object Ext2:
  infix class C[A, B](x: Int)

val  special = Ext2 C 4 //warning: While C is infix, this only affects the type C and not the constructor

infix auxiliary constructors should be either documented, emit a warning, or throw an error

@Sporarum Sporarum added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels May 31, 2023
@Sporarum
Copy link
Contributor Author

Ping @sjrd @smarter as we discussed this briefly in person

@jchyb jchyb added area:specification About the WIP Scala 3 Spec and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Jun 1, 2023
@Sporarum
Copy link
Contributor Author

Sporarum commented Jun 1, 2023

For the reference part, it might be useful to tackle the two following issues at the same time:
#17593
#17429

@Sporarum
Copy link
Contributor Author

Sporarum commented Jun 1, 2023

We discussed with @mbovel, and while this is way too big to be a Spree issue in itself, subparts seem suitable.
If smaller issues are opened, they will be mentioned below

@Sporarum Sporarum added Spree Suitable for a future Spree area:reporting Error reporting including formatting, implicit suggestions, etc area:parser labels Jun 1, 2023
arainko added a commit to arainko/dotty that referenced this issue Jun 13, 2023
Disallow `infix` objects by checking for the `infix` modifier in
`Checking.scala`.

Part of scala#17738
arainko added a commit to arainko/dotty that referenced this issue Jun 17, 2023
mbovel pushed a commit that referenced this issue Jun 19, 2023
Disallow `infix` objects by checking for the `infix` modifier in
`Checking.scala`.

Part of #17738
arainko added a commit to arainko/dotty that referenced this issue Jul 1, 2023
mbovel added a commit that referenced this issue Jul 13, 2023
…7994)

Disallow infix toplevel definitions for anything that is not a `class`,
`typealias`, `match type`, `extension method` and a `trait` (and
`objects` as well but these are handled in their own PR).

Part of #17738
Continuation of #17966

---------

Co-authored-by: Matt Bovel <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:parser area:reporting Error reporting including formatting, implicit suggestions, etc area:specification About the WIP Scala 3 Spec itype:bug Spree Suitable for a future Spree
Projects
None yet
Development

No branches or pull requests

2 participants