From 183b2541b395a9af4c94c9803f315e68e8bad732 Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 1 Oct 2024 16:07:34 +0200 Subject: [PATCH 1/3] Standardize how previously experimental features are handled If a feature was previously experimental and is now standard, we change any tests for that feature to be only dependent on the source version where the feature was standardized. Language imports in old source versions will no longer enable the feature. (And these language imports also come with a deprecation message). If a feature was previously experimental and is now dropped, the feature becomes unavailable also in old versions. The motivation to do it this way is to insist that experimental features are ephemeral. We should not be able to rely on an experimental feature forever in an old version. This commit implements this policy for fewerBraces and clauseInterleaving. Two implemented extensions (relaxedExtensionImports, betterMatchTypeExtractors) already implemented it before. --- compiler/src/dotty/tools/dotc/config/Feature.scala | 9 --------- compiler/src/dotty/tools/dotc/config/SourceVersion.scala | 5 +++++ compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 6 +++--- compiler/src/dotty/tools/dotc/parsing/Scanners.scala | 4 ++-- tests/pos/interleavingExperimental.scala | 4 +--- 5 files changed, 11 insertions(+), 17 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index 8b9a64924ace..444771366726 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -28,7 +28,6 @@ object Feature: val dependent = experimental("dependent") val erasedDefinitions = experimental("erasedDefinitions") val symbolLiterals = deprecated("symbolLiterals") - val fewerBraces = experimental("fewerBraces") val saferExceptions = experimental("saferExceptions") val clauseInterleaving = experimental("clauseInterleaving") val pureFunctions = experimental("pureFunctions") @@ -60,9 +59,7 @@ object Feature: (dependent, "Allow dependent method types"), (erasedDefinitions, "Allow erased definitions"), (symbolLiterals, "Allow symbol literals"), - (fewerBraces, "Enable support for using indentation for arguments"), (saferExceptions, "Enable safer exceptions"), - (clauseInterleaving, "Enable clause interleaving"), (pureFunctions, "Enable pure functions for capture checking"), (captureChecking, "Enable experimental capture checking"), (into, "Allow into modifier on parameter types"), @@ -124,9 +121,6 @@ object Feature: def namedTypeArgsEnabled(using Context) = enabled(namedTypeArguments) - def clauseInterleavingEnabled(using Context) = - sourceVersion.isAtLeast(`3.6`) || enabled(clauseInterleaving) - def betterForsEnabled(using Context) = enabled(betterFors) def genericNumberLiteralsEnabled(using Context) = enabled(genericNumberLiterals) @@ -169,9 +163,6 @@ object Feature: def migrateTo3(using Context): Boolean = sourceVersion == `3.0-migration` - def fewerBracesEnabled(using Context) = - sourceVersion.isAtLeast(`3.3`) || enabled(fewerBraces) - /** If current source migrates to `version`, issue given warning message * and return `true`, otherwise return `false`. */ diff --git a/compiler/src/dotty/tools/dotc/config/SourceVersion.scala b/compiler/src/dotty/tools/dotc/config/SourceVersion.scala index 3200f64fa6f9..84fdeb6bc9f7 100644 --- a/compiler/src/dotty/tools/dotc/config/SourceVersion.scala +++ b/compiler/src/dotty/tools/dotc/config/SourceVersion.scala @@ -31,6 +31,11 @@ enum SourceVersion: def isAtMost(v: SourceVersion) = stable.ordinal <= v.ordinal + def enablesFewerBraces = isAtLeast(`3.3`) + def enablesClauseInterleaving = isAtLeast(`3.6`) + def enablesNewGivens = isAtLeast(`3.6`) + def enablesNamedTuples = isAtLeast(`3.6`) + object SourceVersion extends Property.Key[SourceVersion]: def defaultSourceVersion = `3.7` diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index ef07d477c303..47bc954db5de 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -885,7 +885,7 @@ object Parsers { } }) canRewrite &= (in.isAfterLineEnd || statCtdTokens.contains(in.token)) // test (5) - if canRewrite && (!underColonSyntax || Feature.fewerBracesEnabled) then + if canRewrite && (!underColonSyntax || sourceVersion.enablesFewerBraces) then val openingPatchStr = if !colonRequired then "" else if testChar(startOpening - 1, Chars.isOperatorPart(_)) then " :" @@ -1165,7 +1165,7 @@ object Parsers { * body */ def isColonLambda = - Feature.fewerBracesEnabled && in.token == COLONfollow && followingIsLambdaAfterColon() + sourceVersion.enablesFewerBraces && in.token == COLONfollow && followingIsLambdaAfterColon() /** operand { infixop operand | MatchClause } [postfixop], * @@ -3969,7 +3969,7 @@ object Parsers { val ident = termIdent() var name = ident.name.asTermName val paramss = - if Feature.clauseInterleavingEnabled(using in.languageImportContext) then + if sourceVersion.enablesClauseInterleaving then typeOrTermParamClauses(ParamOwner.Def, numLeadParams) else val tparams = typeParamClauseOpt(ParamOwner.Def) diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index e5bba6c3b73b..ed20c189796b 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -17,7 +17,7 @@ import scala.collection.mutable import scala.collection.immutable.SortedMap import rewrites.Rewrites.patch import config.Feature -import config.Feature.{migrateTo3, fewerBracesEnabled} +import config.Feature.migrateTo3 import config.SourceVersion.{`3.0`, `3.0-migration`} import config.MigrationVersion import reporting.{NoProfile, Profile, Message} @@ -664,7 +664,7 @@ object Scanners { if token == COLONop && inTemplate then report.deprecationWarning(em"`:` after symbolic operator is deprecated; use backticks around operator instead", sourcePos(offset)) true - else token == COLONfollow && (inTemplate || fewerBracesEnabled) + else token == COLONfollow && (inTemplate || sourceVersion.enablesFewerBraces) if enabled then peekAhead() val atEOL = isAfterLineEnd || token == EOF diff --git a/tests/pos/interleavingExperimental.scala b/tests/pos/interleavingExperimental.scala index 63227ef1ebfe..a6b60a237dc3 100644 --- a/tests/pos/interleavingExperimental.scala +++ b/tests/pos/interleavingExperimental.scala @@ -1,5 +1,3 @@ -//> using options --source 3.5 - -import scala.language.experimental.clauseInterleaving +//> using options --source 3.6 def ba[A](x: A)[B](using B): B = summon[B] \ No newline at end of file From 8b00b616060ebf5753e5be2d57e0da579fafa485 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 2 Oct 2024 13:56:55 +0200 Subject: [PATCH 2/3] Update intent CB project to drop fewerBraces setting --- community-build/community-projects/intent | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/community-projects/intent b/community-build/community-projects/intent index 466662fb36ed..c0c4a1939b04 160000 --- a/community-build/community-projects/intent +++ b/community-build/community-projects/intent @@ -1 +1 @@ -Subproject commit 466662fb36ed38d1f045449682bdc109496c6b2d +Subproject commit c0c4a1939b04a6ce4ae5de3aa8949f04674af1f7 From a03a2fc5bd894795dcf8c1050f5ded0f59e96cde Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 18 Feb 2025 16:42:07 +0100 Subject: [PATCH 3/3] Address review comments --- compiler/src/dotty/tools/dotc/config/Feature.scala | 1 - .../dotty/tools/dotc/config/SourceVersion.scala | 2 +- .../src/dotty/tools/dotc/parsing/Parsers.scala | 14 +++++++------- tests/pos/interleavingExperimental.scala | 3 --- 4 files changed, 8 insertions(+), 12 deletions(-) delete mode 100644 tests/pos/interleavingExperimental.scala diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index 444771366726..5ceca43a212b 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -29,7 +29,6 @@ object Feature: val erasedDefinitions = experimental("erasedDefinitions") val symbolLiterals = deprecated("symbolLiterals") val saferExceptions = experimental("saferExceptions") - val clauseInterleaving = experimental("clauseInterleaving") val pureFunctions = experimental("pureFunctions") val captureChecking = experimental("captureChecking") val into = experimental("into") diff --git a/compiler/src/dotty/tools/dotc/config/SourceVersion.scala b/compiler/src/dotty/tools/dotc/config/SourceVersion.scala index 84fdeb6bc9f7..199350949233 100644 --- a/compiler/src/dotty/tools/dotc/config/SourceVersion.scala +++ b/compiler/src/dotty/tools/dotc/config/SourceVersion.scala @@ -34,7 +34,7 @@ enum SourceVersion: def enablesFewerBraces = isAtLeast(`3.3`) def enablesClauseInterleaving = isAtLeast(`3.6`) def enablesNewGivens = isAtLeast(`3.6`) - def enablesNamedTuples = isAtLeast(`3.6`) + def enablesNamedTuples = isAtLeast(`3.7`) object SourceVersion extends Property.Key[SourceVersion]: def defaultSourceVersion = `3.7` diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 47bc954db5de..05d290edf02d 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1011,7 +1011,7 @@ object Parsers { skipParams() lookahead.isColon && { - !sourceVersion.isAtLeast(`3.6`) + !sourceVersion.enablesNewGivens || { // in the new given syntax, a `:` at EOL after an identifier represents a single identifier given // Example: // given C: @@ -1870,7 +1870,7 @@ object Parsers { infixOps(t, canStartInfixTypeTokens, operand, Location.ElseWhere, ParseKind.Type, isOperator = !followingIsVararg() && !isPureArrow - && !(isIdent(nme.as) && sourceVersion.isAtLeast(`3.6`) && inContextBound) + && !(isIdent(nme.as) && sourceVersion.enablesNewGivens && inContextBound) && nextCanFollowOperator(canStartInfixTypeTokens)) /** RefinedType ::= WithType {[nl] Refinement} [`^` CaptureSet] @@ -2263,7 +2263,7 @@ object Parsers { def contextBound(pname: TypeName): Tree = val t = toplevelTyp(inContextBound = true) val ownName = - if isIdent(nme.as) && sourceVersion.isAtLeast(`3.6`) then + if isIdent(nme.as) && sourceVersion.enablesNewGivens then in.nextToken() ident() else EmptyTermName @@ -2276,7 +2276,7 @@ object Parsers { def contextBounds(pname: TypeName): List[Tree] = if in.isColon then in.nextToken() - if in.token == LBRACE && sourceVersion.isAtLeast(`3.6`) + if in.token == LBRACE && sourceVersion.enablesNewGivens then inBraces(commaSeparated(() => contextBound(pname))) else val bound = contextBound(pname) @@ -3500,7 +3500,7 @@ object Parsers { val hkparams = typeParamClauseOpt(ParamOwner.Hk) val bounds = if paramOwner.acceptsCtxBounds then typeAndCtxBounds(name) - else if sourceVersion.isAtLeast(`3.6`) && paramOwner == ParamOwner.Type then typeAndCtxBounds(name) + else if sourceVersion.enablesNewGivens && paramOwner == ParamOwner.Type then typeAndCtxBounds(name) else typeBounds() TypeDef(name, lambdaAbstract(hkparams, bounds)).withMods(mods) } @@ -4069,7 +4069,7 @@ object Parsers { case SEMI | NEWLINE | NEWLINES | COMMA | RBRACE | OUTDENT | EOF => makeTypeDef(typeAndCtxBounds(tname)) case _ if (staged & StageKind.QuotedPattern) != 0 - || sourceVersion.isAtLeast(`3.6`) && in.isColon => + || sourceVersion.enablesNewGivens && in.isColon => makeTypeDef(typeAndCtxBounds(tname)) case _ => syntaxErrorOrIncomplete(ExpectedTypeBoundOrEquals(in.token)) @@ -4244,7 +4244,7 @@ object Parsers { def givenDef(start: Offset, mods: Modifiers, givenMod: Mod) = atSpan(start, nameStart) { var mods1 = addMod(mods, givenMod) val nameStart = in.offset - var newSyntaxAllowed = sourceVersion.isAtLeast(`3.6`) + var newSyntaxAllowed = sourceVersion.enablesNewGivens val hasEmbeddedColon = !in.isColon && followingIsGivenDefWithColon() val name = if isIdent && hasEmbeddedColon then ident() else EmptyTermName diff --git a/tests/pos/interleavingExperimental.scala b/tests/pos/interleavingExperimental.scala deleted file mode 100644 index a6b60a237dc3..000000000000 --- a/tests/pos/interleavingExperimental.scala +++ /dev/null @@ -1,3 +0,0 @@ -//> using options --source 3.6 - -def ba[A](x: A)[B](using B): B = summon[B] \ No newline at end of file