Repository: zio/izumi-reflect
Branch: develop
Commit: 815836839d1d
Files: 141
Total size: 616.4 KB
Directory structure:
gitextract_msmhs6p6/
├── .envrc
├── .git-blame-ignore-revs
├── .gitattributes
├── .github/
│ ├── CODEOWNERS
│ ├── FUNDING.yml
│ └── workflows/
│ └── build.yml
├── .gitignore
├── .jvmopts
├── .mdl/
│ ├── defs/
│ │ └── actions.md
│ └── resources/
│ └── zio-docs.sbt
├── .scala-steward.conf
├── .scalafmt.conf
├── CLA.md
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── build.sbt
├── docs/
│ ├── index.md
│ ├── package.json
│ └── sidebars.js
├── flake.nix
├── fmt-all
├── izumi-reflect/
│ ├── izumi-reflect/
│ │ ├── .js/
│ │ │ └── src/
│ │ │ └── test/
│ │ │ └── scala-2/
│ │ │ └── izumi/
│ │ │ └── reflect/
│ │ │ └── test/
│ │ │ └── PlatformSpecific.scala
│ │ ├── .jvm/
│ │ │ └── src/
│ │ │ └── test/
│ │ │ └── scala-2/
│ │ │ └── izumi/
│ │ │ └── reflect/
│ │ │ └── test/
│ │ │ ├── AllPartsStrongTestScala2Jvm.scala
│ │ │ └── PlatformSpecific.scala
│ │ ├── .native/
│ │ │ └── src/
│ │ │ └── test/
│ │ │ └── scala-2/
│ │ │ └── izumi/
│ │ │ └── reflect/
│ │ │ └── test/
│ │ │ └── PlatformSpecific.scala
│ │ └── src/
│ │ ├── main/
│ │ │ ├── scala/
│ │ │ │ └── izumi/
│ │ │ │ └── reflect/
│ │ │ │ ├── DebugProperties.scala
│ │ │ │ ├── internal/
│ │ │ │ │ └── fundamentals/
│ │ │ │ │ ├── collections/
│ │ │ │ │ │ ├── IzCollections.scala
│ │ │ │ │ │ ├── IzMappings.scala
│ │ │ │ │ │ └── package.scala
│ │ │ │ │ ├── functional/
│ │ │ │ │ │ └── Renderable.scala
│ │ │ │ │ └── platform/
│ │ │ │ │ ├── assertions/
│ │ │ │ │ │ └── IzAssert.scala
│ │ │ │ │ ├── basics/
│ │ │ │ │ │ └── IzBoolean.scala
│ │ │ │ │ ├── console/
│ │ │ │ │ │ └── TrivialLogger.scala
│ │ │ │ │ └── strings/
│ │ │ │ │ └── IzString.scala
│ │ │ │ └── macrortti/
│ │ │ │ ├── LTTOrdering.scala
│ │ │ │ ├── LTTRenderables.scala
│ │ │ │ ├── LTTSyntax.scala
│ │ │ │ ├── LightTypeTag.scala
│ │ │ │ ├── LightTypeTagInheritance.scala
│ │ │ │ ├── LightTypeTagRef.scala
│ │ │ │ └── RuntimeAPI.scala
│ │ │ ├── scala-2/
│ │ │ │ └── izumi/
│ │ │ │ └── reflect/
│ │ │ │ ├── AnnotationTools.scala
│ │ │ │ ├── ReflectionUtil.scala
│ │ │ │ ├── ScalacSink.scala
│ │ │ │ ├── TagMacro.scala
│ │ │ │ ├── Tags.scala
│ │ │ │ ├── TrivialMacroLogger.scala
│ │ │ │ ├── macrortti/
│ │ │ │ │ ├── LTag.scala
│ │ │ │ │ ├── LightTypeTagImpl.scala
│ │ │ │ │ ├── LightTypeTagMacro.scala
│ │ │ │ │ └── package.scala
│ │ │ │ └── package.scala
│ │ │ ├── scala-2.11/
│ │ │ │ └── izumi/
│ │ │ │ └── reflect/
│ │ │ │ └── internal/
│ │ │ │ └── NowarnCompat/
│ │ │ │ └── nowarn.scala
│ │ │ ├── scala-2.12+/
│ │ │ │ └── izumi/
│ │ │ │ └── reflect/
│ │ │ │ └── internal/
│ │ │ │ └── NowarnCompat.scala
│ │ │ ├── scala-2.12-/
│ │ │ │ └── izumi/
│ │ │ │ └── reflect/
│ │ │ │ └── internal/
│ │ │ │ ├── CollectionCompat.scala
│ │ │ │ ├── OrderingCompat.scala
│ │ │ │ └── fundamentals/
│ │ │ │ └── platform/
│ │ │ │ └── language/
│ │ │ │ └── unused.scala
│ │ │ ├── scala-2.13+/
│ │ │ │ └── izumi/
│ │ │ │ └── reflect/
│ │ │ │ └── internal/
│ │ │ │ ├── CollectionCompat.scala
│ │ │ │ ├── OrderingCompat.scala
│ │ │ │ └── fundamentals/
│ │ │ │ └── platform/
│ │ │ │ └── language/
│ │ │ │ └── package.scala
│ │ │ └── scala-3/
│ │ │ └── izumi/
│ │ │ └── reflect/
│ │ │ ├── TagMacro.scala
│ │ │ ├── Tags.scala
│ │ │ ├── dottyreflection/
│ │ │ │ ├── FullDbInspector.scala
│ │ │ │ ├── InheritanceDbInspector.scala
│ │ │ │ ├── Inspect.scala
│ │ │ │ ├── Inspector.scala
│ │ │ │ ├── InspectorBase.scala
│ │ │ │ ├── ReflectionUtil.scala
│ │ │ │ └── TypeInspections.scala
│ │ │ └── macrortti/
│ │ │ ├── LTag.scala
│ │ │ └── package.scala
│ │ └── test/
│ │ ├── scala/
│ │ │ └── izumi/
│ │ │ └── reflect/
│ │ │ ├── internal/
│ │ │ │ └── fundamentals/
│ │ │ │ ├── collections/
│ │ │ │ │ └── IzCollectionsTest.scala
│ │ │ │ └── platform/
│ │ │ │ └── IzStringTest.scala
│ │ │ └── test/
│ │ │ ├── CurrentDottySupportExtentTest.scala
│ │ │ ├── InheritedModel.scala
│ │ │ ├── LTTRenderablesTest.scala
│ │ │ ├── SharedLightTypeTagProgressionTest.scala
│ │ │ ├── SharedLightTypeTagTest.scala
│ │ │ ├── SharedTagProgressionTest.scala
│ │ │ ├── SharedTagTest.scala
│ │ │ ├── TagAssertions.scala
│ │ │ ├── TagCombineTest.scala
│ │ │ ├── TagLogging.scala
│ │ │ ├── TagProgressions.scala
│ │ │ └── TestModel.scala
│ │ ├── scala-2/
│ │ │ └── izumi/
│ │ │ └── reflect/
│ │ │ └── test/
│ │ │ ├── BasicDottyTestMirror.scala
│ │ │ ├── DiscoveryModel.scala
│ │ │ ├── IsScala3.scala
│ │ │ ├── LightTypeTagProgressionTest.scala
│ │ │ ├── LightTypeTagTest.scala
│ │ │ ├── TagProgressionTest.scala
│ │ │ └── TagTest.scala
│ │ ├── scala-2.11/
│ │ │ └── izumi/
│ │ │ └── reflect/
│ │ │ └── test/
│ │ │ └── Scala2MinorVersion.scala
│ │ ├── scala-2.12/
│ │ │ └── izumi/
│ │ │ └── reflect/
│ │ │ └── test/
│ │ │ └── Scala2MinorVersion.scala
│ │ ├── scala-2.13/
│ │ │ └── izumi/
│ │ │ └── reflect/
│ │ │ └── test/
│ │ │ └── Scala2MinorVersion.scala
│ │ ├── scala-2.13+/
│ │ │ └── izumi/
│ │ │ └── reflect/
│ │ │ └── test/
│ │ │ └── Scala213Plus_LightTypeTagTest.scala
│ │ └── scala-3/
│ │ └── izumi/
│ │ └── reflect/
│ │ └── test/
│ │ ├── BasicDottyTest.scala
│ │ ├── DiscoveryModel.scala
│ │ ├── DottyRegressionTests.scala
│ │ ├── IsScala3.scala
│ │ ├── LightTypeTagProgressionTest.scala
│ │ ├── LightTypeTagTest.scala
│ │ ├── PlatformSpecific.scala
│ │ ├── Scala2MinorVersion.scala
│ │ ├── TagProgressionTest.scala
│ │ └── TagTest.scala
│ └── izumi-reflect-thirdparty-boopickle-shaded/
│ ├── .js/
│ │ └── src/
│ │ └── main/
│ │ └── scala/
│ │ └── izumi/
│ │ └── reflect/
│ │ └── thirdparty/
│ │ └── internal/
│ │ └── boopickle/
│ │ ├── ReferenceEquality.scala
│ │ └── StringCodec.scala
│ ├── .jvm/
│ │ └── src/
│ │ └── main/
│ │ └── scala/
│ │ └── izumi/
│ │ └── reflect/
│ │ └── thirdparty/
│ │ └── internal/
│ │ └── boopickle/
│ │ ├── DefaultByteBufferProvider.scala
│ │ ├── ReferenceEquality.scala
│ │ └── StringCodec.scala
│ ├── .native/
│ │ └── src/
│ │ └── main/
│ │ └── scala/
│ │ └── izumi/
│ │ └── reflect/
│ │ └── thirdparty/
│ │ └── internal/
│ │ └── boopickle/
│ │ ├── DefaultByteBufferProvider.scala
│ │ ├── ReferenceEquality.scala
│ │ └── StringCodec.scala
│ └── src/
│ ├── main/
│ │ ├── scala/
│ │ │ └── izumi/
│ │ │ └── reflect/
│ │ │ └── thirdparty/
│ │ │ └── internal/
│ │ │ └── boopickle/
│ │ │ ├── BufferPool.scala
│ │ │ ├── BufferProvider.scala
│ │ │ ├── CodecSize.scala
│ │ │ ├── Codecs.scala
│ │ │ ├── CompositePicklers.scala
│ │ │ ├── Constants.scala
│ │ │ ├── Default.scala
│ │ │ ├── IdentList.scala
│ │ │ ├── IdentMap.scala
│ │ │ ├── Pickler.scala
│ │ │ ├── StringCodecBase.scala
│ │ │ └── TuplePicklers.scala
│ │ ├── scala-2.12-/
│ │ │ └── izumi/
│ │ │ └── reflect/
│ │ │ └── thirdparty/
│ │ │ └── internal/
│ │ │ └── boopickle/
│ │ │ └── XCompat.scala
│ │ └── scala-2.13+/
│ │ └── izumi/
│ │ └── reflect/
│ │ └── thirdparty/
│ │ └── internal/
│ │ └── boopickle/
│ │ └── XCompat.scala
│ └── test/
│ └── scala/
│ └── izumi/
│ └── reflect/
│ └── .keep
├── keys.sh
├── project/
│ ├── Deps.sc
│ ├── Settings.scala
│ ├── Versions.scala
│ ├── build.properties
│ ├── plugins.sbt
│ └── project/
│ └── PluginVersions.scala
├── sbtgen.sc
├── secrets.tar.enc
└── version.sbt
================================================
FILE CONTENTS
================================================
================================================
FILE: .envrc
================================================
use_flake
# Remove git-minimal from PATH to keep system git
PATH_rm '/nix/store/*git-minimal*/bin'
================================================
FILE: .git-blame-ignore-revs
================================================
# Scala Steward: Reformat with scalafmt 3.7.2
edcec351367976c77a5a5ce2842804232e6f7ca5
# Scala Steward: Reformat with scalafmt 3.7.3
d300efb67187b7bfbd1c6f0f04fe181d4373c749
# Scala Steward: Reformat with scalafmt 3.7.4
99666671f20427ffb9b330a3a7ef3d7941d10ad5
# Scala Steward: Reformat with scalafmt 3.8.6
18b438352adfcb8c4aba3cac4c74b783ba28c775
# Scala Steward: Reformat with scalafmt 3.9.1
2150c0a1bfc30f7b4f587a58e74cf92f30d894ac
# Scala Steward: Reformat with scalafmt 3.9.4
fe9b58b7ac2be9ea7dc14c7e1b1b025679b728f4
================================================
FILE: .gitattributes
================================================
* text=auto
.git* text export-ignore
*.sbt text
*.scala text
*.java text
*.txt text
.classpath -text whitespace=cr-at-eol merge=union
*.md text conflict-marker-size=200
*.xml -text whitespace=cr-at-eol
*.sh text eol=lf
================================================
FILE: .github/CODEOWNERS
================================================
* @pshirshov @neko-kai
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: 7mind # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: 7mind # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://www.buymeacoffee.com/7mind'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
================================================
FILE: .github/workflows/build.yml
================================================
name: Build
on:
workflow_dispatch:
inputs: {}
push:
branches: [ "develop" ]
tags: [ "v**" ]
pull_request:
branches: [ "develop" ]
jobs:
checksecret:
runs-on: ubuntu-latest
outputs:
HAVE_SECRETS: ${{ steps.checksecret_job.outputs.HAVE_SECRETS }}
steps:
- id: checksecret_job
env:
OPENSSL_IV: ${{ secrets.OPENSSL_IV }}
OPENSSL_KEY: ${{ secrets.OPENSSL_KEY }}
run: |
echo "HAVE_SECRETS=${{ env.OPENSSL_IV != '' && env.OPENSSL_KEY != '' }}" >> $GITHUB_OUTPUT
build:
runs-on: ubuntu-latest
needs: [ 'checksecret' ]
permissions:
checks: write
contents: write
strategy:
fail-fast: false
matrix:
java: [ '11', '17', '21' ]
scala: [ '2.11', '2.12', '2.13', '3' ]
steps:
- uses: 7mind/github-env@minimal
- name: Build and Test
env:
CI_PULL_REQUEST: ${{ github.event_name == 'pull_request' }}
CI_BRANCH: ${{ github.ref_name }}
CI_BRANCH_TAG: ${{ github.ref_name }}
run: nix develop --command mdl -u java_version:${{ matrix.java }} -u scala_version:${{ matrix.scala }} --github-actions :gen :test
- uses: dorny/test-reporter@v1
if: (needs.checksecret.outputs.HAVE_SECRETS == 'true') && (success() || failure())
with:
name: Test reports (JDK ${{ matrix.java }}, Scala ${{ matrix.scala }})
path: '**/target/test-reports/TEST-*.xml'
reporter: java-junit
- uses: sbt/setup-sbt@v1 # setup sbt
- name: Upload dependency graph
if: needs.checksecret.outputs.HAVE_SECRETS == 'true'
uses: scalacenter/sbt-dependency-submission@53544c3e7bf54b0cb7f375f016e5b7446b96b706
publish-artifacts:
runs-on: ubuntu-latest
needs: [ 'build', 'checksecret' ]
if: needs.checksecret.outputs.HAVE_SECRETS == 'true'
strategy:
matrix:
java: [ '17' ]
scala: [ '2.13' ]
steps:
- uses: 7mind/github-env@minimal
- name: Build and Publish to Sonatype
env:
OPENSSL_IV: ${{ secrets.OPENSSL_IV }}
OPENSSL_KEY: ${{ secrets.OPENSSL_KEY }}
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
CI_PULL_REQUEST: ${{ github.event_name == 'pull_request' }}
CI_BRANCH: ${{ github.ref_name }}
CI_BRANCH_TAG: ${{ github.ref_name }}
run: |
openssl aes-256-cbc -K ${OPENSSL_KEY} -iv ${OPENSSL_IV} -in secrets.tar.enc -out secrets.tar -d
tar xvf secrets.tar
ln -s .secrets/local.sbt local.sbt
rm ./.secrets/credentials.sonatype-nexus.properties
printf "%s\n" "realm=Sonatype Nexus Repository Manager" "host=central.sonatype.com" "user=${SONATYPE_USERNAME}" "password=${SONATYPE_PASSWORD}" > ./.secrets/credentials.sonatype-nexus.properties
nix develop --command mdl -u java_version:${{ matrix.java }} -u scala_version:${{ matrix.scala }} --github-actions :gen :publish-scala
release-and-notify-docs:
name: Release and Notify Docs
runs-on: ubuntu-latest
continue-on-error: false
needs: [ 'build', 'checksecret' ]
if: needs.checksecret.outputs.HAVE_SECRETS == 'true'
#if: ${{ ((github.event_name == 'release') && (github.event.action == 'published')) || (github.event_name == 'workflow_dispatch') }}
strategy:
matrix:
java: [ '17' ]
scala: [ '2.13' ]
steps:
- uses: 7mind/github-env@minimal
- name: Publish Docs to NPM Registry
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
CI_PULL_REQUEST: ${{ github.event_name == 'pull_request' }}
CI_BRANCH: ${{ github.ref_name }}
CI_BRANCH_TAG: ${{ github.ref_name }}
run: nix develop --command mdl -u java_version:${{ matrix.java }} -u scala_version:${{ matrix.scala }} --github-actions :gen :publish-ziodocs
- name: Notify the main repo about the new release of docs package
run: |
PACKAGE_NAME=$(cat docs/package.json | grep '"name"' | awk -F'"' '{print $4}')
PACKAGE_VERSION=$(npm view $PACKAGE_NAME version)
curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: token ${{ secrets.PAT_TOKEN }}"\
https://api.github.com/repos/zio/zio/dispatches \
-d '{
"event_type":"update-docs",
"client_payload":{
"package_name":"'"${PACKAGE_NAME}"'",
"package_version": "'"${PACKAGE_VERSION}"'"
}
}'
all-good:
if: always()
runs-on: ubuntu-latest
needs: [ 'build' ]
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}
================================================
FILE: .gitignore
================================================
.mdl/runs/
.env
.direnv
.hydra
.idea
*.jps
/local.sbt
.secrets*
/config
.agg
.bsp
project/project/project
project/project/metals.sbt
# we cannot exclude build.sbt - scala steward wouldn't work without it
#build.sbt
#project/plugins.sbt
#project/build.properties
secrets.tar
build/project
.DS_Store
.metals
project/metals.sbt
.bloop
.java-version
# Created by .ignore support plugin (hsz.mobi)
### Scala template
*.class
*.log
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-debug/
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
out/
*.iml
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### SBT template
# Simple Build Tool
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control
dist/*
target/
lib_managed/
src_managed/
project/boot/
project/plugins/project/
.history
.cache
.lib/
### Java template
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
.vscode
================================================
FILE: .jvmopts
================================================
# toggle debug output
#-Dizumi.reflect.debug.macro.rtti=true
#-Dizumi.reflect.debug.macro.rtti=false
# toggle debug assertions
#-Dizumi.reflect.debug.macro.rtti.assertions=true
#-Dizumi.reflect.debug.macro.rtti.assertions=false
-Xmx8G
-XX:ReservedCodeCacheSize=256m
-XX:MaxMetaspaceSize=3G
-XX:+OmitStackTraceInFastThrow
-XX:SoftRefLRUPolicyMSPerMB=50
-Dsun.io.useCanonCaches=false
-server
#-Xshare:on
#-Xshare:dump
#-Xlog:class+load=info
-XX:+UseG1GC
# Enable ZGC
#-XX:+UnlockExperimentalVMOptions
#-XX:+UseZGC
# Enable Graal JIT
#-XX:+UnlockExperimentalVMOptions
#-XX:+EnableJVMCI
#-XX:+UseJVMCICompiler
# Bumping JIT inline-level increases performance of Scala code
# https://www.reddit.com/r/scala/comments/cigh0t/these_go_to_eighteen_c_jon_pretty/
# https://scalacenter.github.io/bloop/docs/performance-guide#tweak-the-java-jit
# https://twitter.com/leifwickland/status/1179419045055086595
-XX:MaxInlineLevel=18
# These seem to cause sbt import slowdown :\
#-XX:MaxInlineSize=270
#-XX:MaxTrivialSize=12
================================================
FILE: .mdl/defs/actions.md
================================================
# Build Actions
## Environment
- `LANG=C.UTF-8`
## passthrough
- `HOME`
- `USER`
- `OPENSSL_IV`
- `OPENSSL_KEY`
- `SONATYPE_USERNAME`
- `SONATYPE_PASSWORD`
- `NODE_AUTH_TOKEN`
- `CI_BRANCH_TAG`
- `CI_BUILD_UNIQ_SUFFIX`
- `CI_PULL_REQUEST`
- `CI_BRANCH`
# Axis
- `java_version`=`{11|17*|21}`
- `scala_version`=`{2.12|2.13*|3}`
# action: setup-jdk
Setup JDK path based on JAVA_VERSION
```bash
JAVA_VERSION_VAL=${sys.axis.java_version}
# Determine JAVA_HOME based on JDK version from nix flake environment
# These are set by flake.nix shellHook
case "$JAVA_VERSION_VAL" in
11)
if [[ -n "${JDK11:-}" ]]; then
JAVA_HOME="$JDK11"
else
echo "Error: JDK11 not set in environment" && exit 1
fi
;;
17)
if [[ -n "${JDK17:-}" ]]; then
JAVA_HOME="$JDK17"
else
echo "Error: JDK17 not set in environment" && exit 1
fi
;;
21)
if [[ -n "${JDK21:-}" ]]; then
JAVA_HOME="$JDK21"
else
echo "Error: JDK21 not set in environment" && exit 1
fi
;;
*)
echo "Unsupported JAVA_VERSION: $JAVA_VERSION_VAL" && exit 1
;;
esac
JAVA_BIN="$JAVA_HOME/bin"
PATH="$JAVA_BIN:$PATH"
ret java-home:String="$JAVA_HOME"
ret path:String="$PATH"
```
# action: setup-jvm-options
Setup JVM options and optimizations
```bash
JAVA_OPTIONS="${_JAVA_OPTIONS:-}"
# Add user.home for nix environments
USER_HOME="${env.HOME}"
JAVA_OPTIONS+=" -Duser.home=$USER_HOME"
# Append any custom tail options (optional)
if [[ -n "${JAVA_OPTIONS_TAIL:-}" ]]; then
JAVA_OPTIONS+=" $JAVA_OPTIONS_TAIL"
fi
# Add optimizations
JAVA_OPTIONS+=" -Xmx4000M"
JAVA_OPTIONS+=" -XX:ReservedCodeCacheSize=384M"
JAVA_OPTIONS+=" -XX:NonProfiledCodeHeapSize=256M"
JAVA_OPTIONS+=" -XX:MaxMetaspaceSize=1024M"
# Normalize whitespace
JAVA_OPTIONS=$(echo "$JAVA_OPTIONS" | tr '\n' ' ' | tr -s ' ')
ret java-options:String="$JAVA_OPTIONS"
```
# action: setup-scala
Setup Scala version variables
```bash
SCALA_VERSION_VAL=${sys.axis.scala_version}
VERSION_COMMAND="++ $SCALA_VERSION_VAL"
ret version-command:String="$VERSION_COMMAND"
```
# action: check-sbtgen-staleness
Decide whether sbt build files need regeneration
```bash
PROJECT_ROOT="${sys.project-root}"
readonly SBTGEN_INPUTS=(
"$PROJECT_ROOT/sbtgen.sc"
"$PROJECT_ROOT/project/Deps.sc"
"$PROJECT_ROOT/project/Settings.scala"
"$PROJECT_ROOT/project/Versions.scala"
"$PROJECT_ROOT/project/project/PluginVersions.scala"
)
readonly GENERATED_SBT_FILES=(
"$PROJECT_ROOT/build.sbt"
"$PROJECT_ROOT/project/plugins.sbt"
)
readonly EPOCH_START=0
for input_file in "${SBTGEN_INPUTS[@]}"; do
if [[ ! -f "$input_file" ]]; then
echo "Missing sbtgen input: $input_file"
exit 1
fi
done
for generated_file in "${GENERATED_SBT_FILES[@]}"; do
if [[ ! -f "$generated_file" ]]; then
retain
exit 0
fi
done
newest_input_mtime="$EPOCH_START"
for input_file in "${SBTGEN_INPUTS[@]}"; do
modified_at=$(stat -c %Y "$input_file")
if (( modified_at > newest_input_mtime )); then
newest_input_mtime="$modified_at"
fi
done
oldest_generated_mtime=$(stat -c %Y "${GENERATED_SBT_FILES[0]}")
for generated_file in "${GENERATED_SBT_FILES[@]:1}"; do
modified_at=$(stat -c %Y "$generated_file")
if (( modified_at < oldest_generated_mtime )); then
oldest_generated_mtime="$modified_at"
fi
done
if (( newest_input_mtime > oldest_generated_mtime )); then
retain
fi
```
# action: gen
Generate build files using sbtgen
```bash
# Depend on all setup actions
JAVA_HOME="${action.setup-jdk.java-home}"
PATH="${action.setup-jdk.path}"
JAVA_OPTIONS="${action.setup-jvm-options.java-options}"
_JAVA_OPTIONS="$JAVA_OPTIONS"
bash sbtgen.sc --js --native
```
# action: test
Run tests and binary compatibility checks
```bash
# Declare dependencies and use their outputs
soft action.gen retain.action.check-sbtgen-staleness
JAVA_HOME="${action.setup-jdk.java-home}"
PATH="${action.setup-jdk.path}"
JAVA_OPTIONS="${action.setup-jvm-options.java-options}"
_JAVA_OPTIONS="$JAVA_OPTIONS"
VERSION_COMMAND="${action.setup-scala.version-command}"
sbt -batch -no-colors -v \
--java-home "$JAVA_HOME" \
"$VERSION_COMMAND clean" \
"$VERSION_COMMAND Test/compile" \
"$VERSION_COMMAND test" \
"$VERSION_COMMAND mimaReportBinaryIssues"
```
# action: publish-scala
Publish Scala artifacts to Sonatype (only on release branches/tags)
## vars
- `CI_PULL_REQUEST`
- `CI_BRANCH`
```bash
# Declare dependencies and use their outputs
soft action.gen
JAVA_HOME="${action.setup-jdk.java-home}"
PATH="${action.setup-jdk.path}"
JAVA_OPTIONS="${action.setup-jvm-options.java-options}"
_JAVA_OPTIONS="$JAVA_OPTIONS"
# Get environment variables from mudyla substitution
SONATYPE_USERNAME_VAL="${env.SONATYPE_USERNAME}"
SONATYPE_PASSWORD_VAL="${env.SONATYPE_PASSWORD}"
CI_PULL_REQUEST_VAL="${env.CI_PULL_REQUEST}"
CI_BRANCH_VAL="${env.CI_BRANCH}"
CI_BRANCH_TAG_VAL="${env.CI_BRANCH_TAG}"
# Apply bash defaults
SONATYPE_USERNAME="${SONATYPE_USERNAME_VAL}"
SONATYPE_PASSWORD="${SONATYPE_PASSWORD_VAL}"
CI_PULL_REQUEST="${CI_PULL_REQUEST_VAL:-false}"
CI_BRANCH="${CI_BRANCH_VAL}"
CI_BRANCH_TAG="${CI_BRANCH_TAG_VAL}"
if [[ -z "$SONATYPE_USERNAME" ]]; then
echo "Missing SONATYPE_USERNAME, skipping publish"
exit 0
fi
if [[ -z "$SONATYPE_PASSWORD" ]]; then
echo "Missing SONATYPE_PASSWORD, skipping publish"
exit 0
fi
if [[ "$CI_PULL_REQUEST" == "true" ]]; then
echo "Publishing not allowed on P/Rs"
exit 0
fi
if [[ "$CI_BRANCH" != "develop" && ! "$CI_BRANCH_TAG" =~ ^v ]]; then
echo "Publishing not allowed (CI_BRANCH=$CI_BRANCH, CI_BRANCH_TAG=$CI_BRANCH_TAG)"
exit 0
fi
if [[ "$CI_BRANCH_TAG" =~ ^v.*$ ]]; then
# Full release with sonaRelease
sbt -batch -no-colors -v \
--java-home "$JAVA_HOME" \
"show credentials" \
"+clean" \
"+test:compile" \
"+publishSigned" \
"sonaRelease"
else
# Snapshot publish without release
sbt -batch -no-colors -v \
--java-home "$JAVA_HOME" \
"show credentials" \
"+clean" \
"+test:compile" \
"+publishSigned"
fi
```
# action: publish-ziodocs
Publish documentation to NPM
## vars
- `CI_PULL_REQUEST`
- `CI_BRANCH`
```bash
# Declare dependencies and use their outputs
soft action.gen
JAVA_HOME="${action.setup-jdk.java-home}"
PATH="${action.setup-jdk.path}"
JAVA_OPTIONS="${action.setup-jvm-options.java-options}"
_JAVA_OPTIONS="$JAVA_OPTIONS"
# Get environment variables from mudyla substitution
NODE_AUTH_TOKEN_VAL="${env.NODE_AUTH_TOKEN}"
CI_PULL_REQUEST_VAL="${env.CI_PULL_REQUEST}"
CI_BRANCH_VAL="${env.CI_BRANCH}"
CI_BRANCH_TAG_VAL="${env.CI_BRANCH_TAG}"
# Apply bash defaults
NODE_AUTH_TOKEN="${NODE_AUTH_TOKEN_VAL}"
CI_PULL_REQUEST="${CI_PULL_REQUEST_VAL:-false}"
CI_BRANCH="${CI_BRANCH_VAL}"
CI_BRANCH_TAG="${CI_BRANCH_TAG_VAL}"
if [[ -z "$NODE_AUTH_TOKEN" ]]; then
echo "Missing NODE_AUTH_TOKEN, skipping docs publish"
exit 0
fi
if [[ "$CI_PULL_REQUEST" == "true" ]]; then
echo "Publishing not allowed on P/Rs"
exit 0
fi
if [[ "$CI_BRANCH" != "develop" && ! "$CI_BRANCH_TAG" =~ ^v ]]; then
echo "Publishing not allowed (CI_BRANCH=$CI_BRANCH, CI_BRANCH_TAG=$CI_BRANCH_TAG)"
exit 0
fi
# Copy zio-docs.sbt
cp ${sys.project-root}/.mdl/resources/zio-docs.sbt ${sys.project-root}/zio-docs.sbt
# Extract docs section from README and append to docs/index.md
awk '//,//' ${sys.project-root}/README.md >> ${sys.project-root}/docs/index.md
sed -i '//d' ${sys.project-root}/docs/index.md
sed -i '//d' ${sys.project-root}/docs/index.md
# Setup npm auth
echo "//registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN" > ~/.npmrc
# Verify npm authentication
npm whoami
# Publish to npm
sbt -batch -no-colors -v \
--java-home "$JAVA_HOME" \
docs/publishToNpm
```
# action: build
Full build pipeline - generate and test
```bash
soft action.gen retain.action.check-sbtgen-staleness
dep action.test
exit 0
```
================================================
FILE: .mdl/resources/zio-docs.sbt
================================================
lazy val docs = project
.in(file("izumi-reflect-docs"))
.settings(
publish / skip := true,
moduleName := "izumi-reflect-docs",
scalacOptions -= "-Yno-imports",
scalacOptions -= "-Xfatal-warnings",
projectName := "izumi-reflect",
mainModuleName := "izumi-reflect",
projectStage := ProjectStage.ProductionReady,
)
.enablePlugins(WebsitePlugin)
================================================
FILE: .scala-steward.conf
================================================
updates.pin = [
# do not update Scala 2.13 because of revoked forward compatibility
{ groupId = "org.scala-lang", artifactId="scala-compiler", version = "2.13.14" },
{ groupId = "org.scala-lang", artifactId="scala-library", version = "2.13.14" },
{ groupId = "org.scala-lang", artifactId="scala-reflect", version = "2.13.14" },
# use only Scala3 3.3 LTS
{ groupId = "org.scala-lang", artifactId="scala3-compiler", version = "3.3." },
{ groupId = "org.scala-lang", artifactId="scala3-library", version = "3.3." },
{ groupId = "org.scala-lang", artifactId="scala3-library_sjs1", version = "3.3." }
]
pullRequests.frequency = "@monthly"
pullRequests.grouping = [
# group scala updates & scoverage
{ name="scala-versions", "title"="Scala compiler updates", "filter"=[{"group" = "org.scala-lang"}, {"group" = "org.scoverage"}] },
# group everything else
{ name = "all", "title" = "Dependency updates", "filter" = [{"group" = "*"}] }
]
updatePullRequests = "always"
================================================
FILE: .scalafmt.conf
================================================
version = "3.9.4"
runner.dialect = scala3
project.git = true
project.excludePaths = ["glob:**.sbt", "glob:**sbtgen.sc"]
maxColumn = 170
importSelectors = singleLine
spaces.inImportCurlyBraces = false
literals.hexDigits = "Upper"
align.preset = none
align.stripMargin = true
align.inInterpolation = false
newlines.beforeCurlyLambdaParams = multiline
newlines.afterCurlyLambdaParams = never
newlines.implicitParamListModifierPrefer = after
newlines.avoidAfterYield = true
newlines.alwaysBeforeMultilineDef = false
newlines.inInterpolation = allow
indent.defnSite = 2
indent.callSite = 2
indent.extendSite = 2
assumeStandardLibraryStripMargin = true
docstrings = ScalaDoc
docstrings.wrap = false
docstrings.blankFirstLine = keep
docstrings.forceBlankLineBefore = false
lineEndings = unix
danglingParentheses.callSite = true
danglingParentheses.defnSite = true
danglingParentheses.ctrlSite = false
danglingParentheses.exclude = []
verticalAlignMultilineOperators = true
includeCurlyBraceInSelectChains = true
includeNoParensInSelectChains = true
verticalMultiline.atDefnSite = true
verticalMultiline.arityThreshold = 100
verticalMultiline.newlineAfterOpenParen = false
optIn.configStyleArguments = true
optIn.breaksInsideChains = true
optIn.breakChainOnFirstMethodDot = true
optIn.selfAnnotationNewline = true
optIn.annotationNewlines = true
rewrite.rules = [AsciiSortImports, RedundantBraces, RedundantParens]
rewrite.redundantBraces.methodBodies = false // remove braces only in interpolations
rewrite.redundantBraces.maxLines = -1 // remove braces only in interpolations
rewrite.redundantBraces.generalExpressions = false // remove braces only in interpolations
rewrite.redundantBraces.includeUnitMethods = false
rewrite.redundantBraces.stringInterpolation = true
rewrite.redundantBraces.parensForOneLineApply = true
//rewrite.trailingCommas.trailingCommas = multiple
rewrite.trailingCommas.style = never // Scala 2.11
================================================
FILE: CLA.md
================================================
# ZIO Contributor License Agreement
Thank you for your interest in contributing to the ZIO open source project.
This contributor agreement ("Agreement") describes the terms and conditions under which you may Submit a Contribution to Us. By Submitting a Contribution to Us, you accept the terms and conditions in the Agreement. If you do not accept the terms and conditions in the Agreement, you must not Submit any Contribution to Us.
This is a legally binding document, so please read it carefully before accepting the terms and conditions. If you accept this Agreement, the then-current version of this Agreement shall apply each time you Submit a Contribution. The Agreement may cover more than one software project managed by Us.
## 1. Definitions
"We" or "Us" means Ziverge, Inc., and its duly appointed and authorized representatives.
"You" means the individual or entity who Submits a Contribution to Us.
"Contribution" means any work of authorship that is Submitted by You to Us in which You own or assert ownership of the Copyright. You may not Submit a Contribution if you do not own the Copyright in the entire work of authorship.
"Copyright" means all rights protecting works of authorship owned or controlled by You, including copyright, moral and neighboring rights, as appropriate, for the full term of their existence including any extensions by You.
"Material" means the work of authorship which is made available by Us to third parties. When this Agreement covers more than one software project, the Material means the work of authorship to which the Contribution was Submitted. After You Submit the Contribution, it may be included in the Material.
"Submit" means any form of electronic, verbal, or written communication sent to Us or our representatives, including but not limited to electronic mailing lists, electronic mail, source code control systems, pull requests, and issue tracking systems that are managed by, or on behalf of, Us for the purpose of discussing and improving the Material, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
"Submission Date" means the date on which You Submit a Contribution to Us.
"Effective Date" means the earliest date You execute this Agreement by Submitting a Contribution to Us.
## 2. Grant of Rights
### 2.1 Copyright License
2.1.1. You retain ownership of the Copyright in Your Contribution and have the same rights to use or license the Contribution which You would have had without entering into the Agreement.
2.1.2. To the maximum extent permitted by the relevant law, You grant to Us a perpetual, worldwide, non-exclusive, transferable, royalty-free, irrevocable license under the Copyright covering the Contribution, with the right to sublicense such rights through multiple tiers of sublicensees, to reproduce, modify, display, perform and distribute the Contribution as part of the Material; provided that this license is conditioned upon compliance with Section 2.3.
### 2.2 Patent License
For patent claims including, without limitation, method, process, and apparatus claims which You own, control or have the right to grant, now or in the future, You grant to Us a perpetual, worldwide, non-exclusive, transferable, royalty-free, irrevocable patent license, with the right to sublicense these rights to multiple tiers of sublicensees, to make, have made, use, sell, offer for sale, import and otherwise transfer the Contribution and the Contribution in combination with the Material (and portions of such combination). This license is granted only to the extent that the exercise of the licensed rights infringes such patent claims; and provided that this license is conditioned upon compliance with Section 2.3.
### 2.3 Outbound License
Based on the grant of rights in Sections 2.1 and 2.2, if We include Your Contribution in a Material, We may license the Contribution under any license, including copyleft, permissive, commercial, or proprietary licenses. As a condition on the exercise of this right, We agree to also license the Contribution under the terms of the license or licenses which We are using for the Material on the Submission Date.
### 2.4 Moral Rights
If moral rights apply to the Contribution, to the maximum extent permitted by law, You waive and agree not to assert such moral rights against Us or our successors in interest, or any of our licensees, either direct or indirect.
### 2.5 Our Rights
You acknowledge that We are not obligated to use Your Contribution as part of the Material and may decide to include any Contribution We consider appropriate.
### 2.6 Reservation of Rights
Any rights not expressly licensed under this section are expressly reserved by You.
## 3. Agreement
You confirm that:
a. You have the legal authority to enter into this Agreement.
b. You own the Copyright and patent claims covering the Contribution which are required to grant the rights under Section 2.
c. The grant of rights under Section 2 does not violate any grant of rights which You have made to third parties, including Your employer. If You are an employee, You have had Your employer approve this Agreement or sign the Entity version of this document. If You are less than eighteen years old, please have Your parents or guardian sign the Agreement.
d. You have followed the instructions in, if You do not own the Copyright in the entire work of authorship Submitted.
## 4. Disclaimer
EXCEPT FOR THE EXPRESS WARRANTIES IN SECTION 3, THE CONTRIBUTION IS PROVIDED "AS IS". MORE PARTICULARLY, ALL EXPRESS OR IMPLIED WARRANTIES INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY DISCLAIMED BY YOU TO US. TO THE EXTENT THAT ANY SUCH WARRANTIES CANNOT BE DISCLAIMED, SUCH WARRANTY IS LIMITED IN DURATION TO THE MINIMUM PERIOD PERMITTED BY LAW.
## 5. Consequential Damage Waiver
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF ANTICIPATED SAVINGS, LOSS OF DATA, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL AND EXEMPLARY DAMAGES ARISING OUT OF THIS AGREEMENT REGARDLESS OF THE LEGAL OR EQUITABLE THEORY (CONTRACT, TORT OR OTHERWISE) UPON WHICH THE CLAIM IS BASED.
## 6. Miscellaneous
6.1. This Agreement will be governed by and construed in accordance with the laws of the state of Maryland, in the United States of America, excluding its conflicts of law provisions. Under certain circumstances, the governing law in this section might be superseded by the United Nations Convention on Contracts for the International Sale of Goods ("UN Convention") and the parties intend to avoid the application of the UN Convention to this Agreement and, thus, exclude the application of the UN Convention in its entirety to this Agreement.
6.2. This Agreement sets out the entire agreement between You and Us for Your Contributions to Us and overrides all other agreements or understandings.
6.3. If You or We assign the rights or obligations received through this Agreement to a third party, as a condition of the assignment, that third party must agree in writing to abide by all the rights and obligations in the Agreement.
6.4. The failure of either party to require performance by the other party of any provision of this Agreement in one situation shall not affect the right of a party to require such performance at any time in the future. A waiver of performance under a provision in one situation shall not be considered a waiver of the performance of the provision in the future or a waiver of the provision in its entirety.
6.5. If any provision of this Agreement is found void and unenforceable, such provision will be replaced to the extent possible with a provision that comes closest to the meaning of the original provision and which is enforceable. The terms and conditions set forth in this Agreement shall apply notwithstanding any failure of essential purpose of this Agreement or any limited remedy to the maximum extent possible under law.
================================================
FILE: CLAUDE.md
================================================
**IMPORTANT** All the conversations must be done in Irish.
They will be reviewed by Irish speakers who doesn't speak English very well!
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at pshirshov@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
================================================
FILE: LICENSE
================================================
Licensed under the Apache License, Version 2.0 (the "License");
You may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
[![Project stage][Stage]][Stage-Page]
[](https://github.com/zio/izumi-reflect/actions/workflows/build.yml)
[](https://javadoc.io/doc/dev.zio/izumi-reflect_2.13)
[](https://github.com/zio/izumi-reflect/releases)
[](https://search.maven.org/search?q=g%3Adev.zio+a%3Aizumi-reflect)
[](https://index.scala-lang.org/zio/izumi-reflect)
---
Please consider supporting our work through GitHub Sponsors.
---
# izumi-reflect
> @quote: Looks a bit similar to TypeTag
`izumi-reflect` is a fast, lightweight, portable and efficient alternative for `TypeTag` from `scala-reflect`.
`izumi-reflect` is a lightweight model of Scala type system and provides a simulator of the important parts of the Scala typechecker.
## Why `izumi-reflect`
1. `izumi-reflect` compiles faster, runs a lot faster than `scala-reflect` and is fully immutable and [thread-safe](https://github.com/scala/bug/issues/10766),
2. `izumi-reflect` supports Scala 2.11, 2.12, 2.13 and **Scala 3**,
3. `izumi-reflect` supports Scala.js and Scala Native,
4. `izumi-reflect` works well with [GraalVM Native Image](https://www.graalvm.org/reference-manual/native-image/),
5. `izumi-reflect` allows you to obtain tags for unapplied type constructors (`F[_]`) and combine them at runtime.
## Credits
`izumi-reflect` has been created by [Septimal Mind](https://7mind.io) to power [Izumi Project](https://github.com/7mind/izumi),
as a replacement for `TypeTag` in reaction to a lack of confirmed information about the future of `scala-reflect`/`TypeTag` in Scala 3 ([Motivation](https://blog.7mind.io/lightweight-reflection.html)), and donated to ZIO.
## Limitations
`izumi-reflect` model of the Scala type system is not 100% precise, but "good enough" for the vast majority of the usecases.
Known limitations are:
1. Recursive type bounds (F-bounded types) are not preserved and may produce false positives,
2. Existential types, both written with wildcards and `forSome` may produce unexpected results, the support is limited,
3. Path-Dependent Types are based on variable names and may cause unexpected results when variables with different names have the same type or vice-versa (vs. Scala compiler)
4. This-Types such as `X.this.type` are ignored and identical to `X`
5. `izumi-reflect` is less powerful than `scala-reflect`: it does not preserve fields and methods when it's not necessary for equality and subtype checks, it does not preserve code trees, internal compiler data structures, etc.
6. There are some optimizations in place which reduce correctness, namely: subtype check for `scala.Matchable` will always return true, no distinction is made between `scala.Any` and `scala.AnyRef`, no distinction is made between `scala.Nothing` and `scala.Null`. In other words, the library does not recognize that `Null` is not a subtype of `AnyVal`s.
7. Lower bounds are not preserved in abstract higher-kinded type members which may produce false comparisons.
8. Type and value members are not preserved in concrete types which may produce false comparisons with refined/structural types. (https://github.com/zio/izumi-reflect/issues/481)
## Debugging
Set [`-Dizumi.reflect.debug.macro.rtti=true`](https://javadoc.io/doc/dev.zio/izumi-reflect_2.13/latest/izumi/reflect/DebugProperties$.html#izumi.reflect.debug.macro.rtti:String(%22izumi.reflect.debug.macro.rtti%22)) to enable debug output during compilation when tags are constructed and at runtime when they are compared.
```shell
sbt -Dizumi.reflect.debug.macro.rtti=true
```
To see debug output when compiling in Intellij, add the above flag to `VM options` in [Preferences -> Build, Execution, Deployment -> Compiler -> Scala Compiler -> Scala Compile Server](jetbrains://idea/settings?name=Build%2C+Execution%2C+Deployment--Compiler--Scala+Compiler--Scala+Compile+Server)
You may also set it in `.jvmopts` file during development. (`.jvmopts` properties will not apply to Intellij compile server, only to sbt)
Set `-Dizumi.reflect.debug.macro.rtti.assertions=true` to enable additional assertions.
Other useful system properties are:
- [`izumi.reflect.rtti.optimized.equals`](https://javadoc.io/doc/dev.zio/izumi-reflect_2.13/latest/izumi/reflect/DebugProperties$.html#izumi.reflect.rtti.optimized.equals:String(%22izumi.reflect.rtti.optimized.equals%22))
- [`izumi.reflect.rtti.cache.compile`](https://javadoc.io/doc/dev.zio/izumi-reflect_2.13/latest/izumi/reflect/DebugProperties$.html#izumi.reflect.rtti.cache.compile:String(%22izumi.reflect.rtti.cache.compile%22))
## Build
`build.sbt` is generated by [sbtgen](https://github.com/7mind/sbtgen). During development you may not want to mess with ScalaJS and ScalaNative, you may generate a pure-JVM Scala project:
```bash
./sbtgen.sc
```
Once you finished tinkering with the code you may want to generate full project and test it for all the platforms:
```bash
./sbtgen.sc --js --native
sbt +test
```
To develop using Scala 2 invoke sbtgen with a scala version argument:
```bash
./sbtgen.sc 2 // 2.13
./sbtgen.sc 2.12 // 2.12
```
Likewise with Scala 3:
```bash
./sbtgen.sc 3
```
In Intellij, you may also set Scala version by changing the option `sbt -> sbt settings -> Open cross-compiled projects Scala 3 / Scala 2 projects as:`
Provided `flake.nix` can be used to set up the external dependencies necessary to build the project, such as sbt, JDK, coursier, etc:
```bash
nix develop
```
The project's CI is built with [mudyla](https://github.com/7mind/mudyla), you may reproduce CI build locally with:
```bash
nix develop --command mdl --github-actions :gen :test
```
Available mudyla actions are defined in [.mdl/defs/*.md](.mdl/defs/)
# Talks
* [Kit Langton — Scala 3 Macro Fun (Open Source Hackery)](https://www.youtube.com/watch?v=wsLhjqCKZuU)
* [Pavel Shirshov — Izumi Reflect: Scala Type System Model](https://www.youtube.com/watch?v=ShRfzVz58OY)
# See also
## [`gzoller/scala-reflection`](https://github.com/gzoller/scala-reflection)
* Scala 3 only
* No support for subtype checks
* Type lambdas are not supported
* _Preserves field information_
## [`airframe-surface`](https://wvlet.org/airframe/docs/airframe-surface)
* Scala 2 and Scala 3
* No support for subtype checks
* _Preserves field information_
## And even more
1. https://github.com/gaeljw/typetrees - a very basic type tag substitute for Scala 3
2. https://stackoverflow.com/questions/75752812/is-there-a-simple-scala-3-example-of-how-to-use-quoted-type-as-replacement-for - discussion on StackOverflow
3. https://contributors.scala-lang.org/t/scala-3-and-reflection/3627 - original discussion on Scala Contributors forum
[Stage]: https://img.shields.io/badge/Project%20Stage-Production%20Ready-brightgreen.svg
[Stage-Page]: https://github.com/zio/zio/wiki/Project-Stages
================================================
FILE: build.sbt
================================================
// DO NOT EDIT THIS FILE
// IT IS AUTOGENERATED BY `sbtgen.sc` SCRIPT
// ALL CHANGES WILL BE LOST
import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType}
import com.typesafe.tools.mima.core._
enablePlugins(SbtgenVerificationPlugin)
lazy val `izumi-reflect-thirdparty-boopickle-shaded` = crossProject(JVMPlatform, JSPlatform, NativePlatform).crossType(CrossType.Pure).in(file("izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded"))
.settings(
libraryDependencies ++= Seq(
"org.scalatest" %%% "scalatest" % V.scalatest % Test
),
libraryDependencies ++= { if (scalaVersion.value.startsWith("2.")) Seq(
compilerPlugin("org.typelevel" % "kind-projector" % V.kind_projector cross CrossVersion.full),
"org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided
) else Seq.empty },
libraryDependencies ++= {
val version = scalaVersion.value
if (version.startsWith("0.") || version.startsWith("3.")) {
Seq(
"org.scala-lang" %% "scala3-compiler" % scalaVersion.value % Provided
)
} else Seq.empty
}
)
.settings(
organization := "dev.zio",
Compile / unmanagedSourceDirectories ++= {
val version = scalaVersion.value
val crossVersions = crossScalaVersions.value
import Ordering.Implicits._
val ltEqVersions = crossVersions.map(CrossVersion.partialVersion).filter(_ <= CrossVersion.partialVersion(version)).flatten
(Compile / unmanagedSourceDirectories).value.flatMap {
case dir if dir.getPath.endsWith("scala") => ltEqVersions.map { case (m, n) => file(dir.getPath + s"-$m.$n+") }
case _ => Seq.empty
}
},
Test / unmanagedSourceDirectories ++= {
val version = scalaVersion.value
val crossVersions = crossScalaVersions.value
import Ordering.Implicits._
val ltEqVersions = crossVersions.map(CrossVersion.partialVersion).filter(_ <= CrossVersion.partialVersion(version)).flatten
(Test / unmanagedSourceDirectories).value.flatMap {
case dir if dir.getPath.endsWith("scala") => ltEqVersions.map { case (m, n) => file(dir.getPath + s"-$m.$n+") }
case _ => Seq.empty
}
},
Compile / unmanagedSourceDirectories ++= {
val version = scalaVersion.value
val crossVersions = crossScalaVersions.value
import Ordering.Implicits._
val gtEqVersions = crossVersions.map(CrossVersion.partialVersion).filter(_ >= CrossVersion.partialVersion(version)).flatten
(Compile / unmanagedSourceDirectories).value.flatMap {
case dir if dir.getPath.endsWith("scala") => gtEqVersions.map { case (m, n) => file(dir.getPath + s"-$m.$n-") }
case _ => Seq.empty
}
},
Test / unmanagedSourceDirectories ++= {
val version = scalaVersion.value
val crossVersions = crossScalaVersions.value
import Ordering.Implicits._
val gtEqVersions = crossVersions.map(CrossVersion.partialVersion).filter(_ >= CrossVersion.partialVersion(version)).flatten
(Test / unmanagedSourceDirectories).value.flatMap {
case dir if dir.getPath.endsWith("scala") => gtEqVersions.map { case (m, n) => file(dir.getPath + s"-$m.$n-") }
case _ => Seq.empty
}
},
Compile / unmanagedSourceDirectories ++= {
val crossVersions = crossScalaVersions.value
import Ordering.Implicits._
val ltEqVersions = crossVersions.map(CrossVersion.partialVersion).sorted.flatten
def joinV = (_: Product).productIterator.mkString(".")
val allRangeVersions = (2 to math.max(2, ltEqVersions.size))
.flatMap(i => ltEqVersions.sliding(i).filter(_.size == i))
.map(l => (l.head, l.last))
.distinct
CrossVersion.partialVersion(scalaVersion.value).toList.flatMap {
version =>
val rangeVersions = allRangeVersions
.filter { case (l, r) => l <= version && version <= r }
.map { case (l, r) => s"-${joinV(l)}-${joinV(r)}" }
(Compile / unmanagedSourceDirectories).value.flatMap {
case dir if dir.getPath.endsWith("scala") => rangeVersions.map { vStr => file(dir.getPath + vStr) }
case _ => Seq.empty
}
}
},
Test / unmanagedSourceDirectories ++= {
val crossVersions = crossScalaVersions.value
import Ordering.Implicits._
val ltEqVersions = crossVersions.map(CrossVersion.partialVersion).sorted.flatten
def joinV = (_: Product).productIterator.mkString(".")
val allRangeVersions = (2 to math.max(2, ltEqVersions.size))
.flatMap(i => ltEqVersions.sliding(i).filter(_.size == i))
.map(l => (l.head, l.last))
.distinct
CrossVersion.partialVersion(scalaVersion.value).toList.flatMap {
version =>
val rangeVersions = allRangeVersions
.filter { case (l, r) => l <= version && version <= r }
.map { case (l, r) => s"-${joinV(l)}-${joinV(r)}" }
(Test / unmanagedSourceDirectories).value.flatMap {
case dir if dir.getPath.endsWith("scala") => rangeVersions.map { vStr => file(dir.getPath + vStr) }
case _ => Seq.empty
}
}
},
Compile / doc / sources := { (isSnapshot.value, scalaVersion.value) match {
case (_, "3.3.6") => Seq(
)
case (_, _) => (Compile / doc / sources).value
} },
Test / testOptions += Tests.Argument("-oDF"),
scalacOptions ++= { (isSnapshot.value, scalaVersion.value) match {
case (_, "2.11.12") => Seq.empty
case (_, "2.12.20") => Seq(
"-release:8",
"-explaintypes",
"-Ypartial-unification",
if (insideCI.value) "-Wconf:any:error" else "-Wconf:any:warning",
"-Wconf:cat=optimizer:warning",
"-Wconf:cat=other-match-analysis:error",
"-Ybackend-parallelism",
math.min(16, math.max(1, sys.runtime.availableProcessors() - 1)).toString,
"-Xlint:adapted-args",
"-Xlint:by-name-right-associative",
"-Xlint:constant",
"-Xlint:delayedinit-select",
"-Xlint:doc-detached",
"-Xlint:inaccessible",
"-Xlint:infer-any",
"-Xlint:missing-interpolator",
"-Xlint:nullary-override",
"-Xlint:nullary-unit",
"-Xlint:option-implicit",
"-Xlint:package-object-classes",
"-Xlint:poly-implicit-overload",
"-Xlint:private-shadow",
"-Xlint:stars-align",
"-Xlint:type-parameter-shadow",
"-Xlint:unsound-match",
"-opt-warnings:_",
"-Ywarn-extra-implicit",
"-Ywarn-unused:_",
"-Ywarn-adapted-args",
"-Ywarn-dead-code",
"-Ywarn-inaccessible",
"-Ywarn-infer-any",
"-Ywarn-nullary-override",
"-Ywarn-nullary-unit",
"-Ywarn-numeric-widen",
"-Ywarn-unused-import",
"-Ywarn-value-discard",
"-Ycache-plugin-class-loader:always",
"-Ycache-macro-class-loader:last-modified",
"-Wconf:msg=nowarn:silent"
)
case (_, "2.13.14") => Seq(
"-release:8",
"-explaintypes",
if (insideCI.value) "-Wconf:any:error" else "-Wconf:any:warning",
"-Wconf:cat=optimizer:warning",
"-Wconf:cat=other-match-analysis:error",
"-Vtype-diffs",
"-Ybackend-parallelism",
math.min(16, math.max(1, sys.runtime.availableProcessors() - 1)).toString,
"-Wdead-code",
"-Wextra-implicit",
"-Wnumeric-widen",
"-Woctal-literal",
"-Wvalue-discard",
"-Wunused:_",
"-Wmacros:default",
"-Ycache-plugin-class-loader:always",
"-Ycache-macro-class-loader:last-modified",
"-Wconf:msg=nowarn:silent"
)
case (_, _) => Seq(
"-Ykind-projector",
"-no-indent",
"-language:implicitConversions"
)
} },
scalacOptions -= "-Wconf:any:error",
mimaPreviousArtifacts := { (isSnapshot.value, scalaVersion.value) match {
case (_, "3.3.6") => Set(organization.value %% name.value % "2.2.5", organization.value %% name.value % "2.1.0")
case (_, _) => Set(organization.value %% name.value % "2.2.5", organization.value %% name.value % "2.1.0", organization.value %% name.value % "1.0.0")
} },
scalacOptions ++= { (isSnapshot.value, scalaVersion.value) match {
case (_, "2.13.14") => Seq(
"-Xlint:-implicit-recursion"
)
case (_, _) => Seq.empty
} },
scalacOptions ++= { (isSnapshot.value, scalaVersion.value) match {
case (false, "2.12.20") => Seq(
"-opt:l:inline",
"-opt-inline-from:izumi.reflect.**"
)
case (false, "2.13.14") => Seq(
"-opt:l:inline",
"-opt-inline-from:izumi.reflect.**"
)
case (_, _) => Seq.empty
} },
Compile / packageBin / packageOptions += Package.ManifestAttributes("Automatic-Module-Name" -> s"${organization.value.replaceAll("-",".")}.${moduleName.value.replaceAll("-",".")}"),
Compile / scalacOptions --= Seq("-Ywarn-value-discard","-Ywarn-unused:_", "-Wvalue-discard", "-Wunused:_")
)
.jvmSettings(
crossScalaVersions := Seq(
"3.3.6",
"2.13.14",
"2.12.20",
"2.11.12"
),
scalaVersion := crossScalaVersions.value.head
)
.jsSettings(
crossScalaVersions := Seq(
"3.3.6",
"2.13.14",
"2.12.20"
),
scalaVersion := crossScalaVersions.value.head,
coverageEnabled := false,
scalaJSLinkerConfig := scalaJSLinkerConfig.value.withModuleKind(ModuleKind.CommonJSModule)
)
.nativeSettings(
crossScalaVersions := Seq(
"3.3.6",
"2.13.14",
"2.12.20"
),
scalaVersion := crossScalaVersions.value.head,
coverageEnabled := false,
test := {},
Test / test := {}
)
lazy val `izumi-reflect-thirdparty-boopickle-shadedJVM` = `izumi-reflect-thirdparty-boopickle-shaded`.jvm
lazy val `izumi-reflect-thirdparty-boopickle-shadedJS` = `izumi-reflect-thirdparty-boopickle-shaded`.js
lazy val `izumi-reflect-thirdparty-boopickle-shadedNative` = `izumi-reflect-thirdparty-boopickle-shaded`.native
lazy val `izumi-reflect` = crossProject(JVMPlatform, JSPlatform, NativePlatform).crossType(CrossType.Pure).in(file("izumi-reflect/izumi-reflect"))
.dependsOn(
`izumi-reflect-thirdparty-boopickle-shaded` % "test->compile;compile->compile"
)
.settings(
libraryDependencies ++= Seq(
"org.scalatest" %%% "scalatest" % V.scalatest % Test
),
libraryDependencies ++= { if (scalaVersion.value.startsWith("2.")) Seq(
compilerPlugin("org.typelevel" % "kind-projector" % V.kind_projector cross CrossVersion.full),
"org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided
) else Seq.empty },
libraryDependencies ++= {
val version = scalaVersion.value
if (version.startsWith("0.") || version.startsWith("3.")) {
Seq(
"org.scala-lang" %% "scala3-compiler" % scalaVersion.value % Provided
)
} else Seq.empty
}
)
.settings(
organization := "dev.zio",
Compile / unmanagedSourceDirectories ++= {
val version = scalaVersion.value
val crossVersions = crossScalaVersions.value
import Ordering.Implicits._
val ltEqVersions = crossVersions.map(CrossVersion.partialVersion).filter(_ <= CrossVersion.partialVersion(version)).flatten
(Compile / unmanagedSourceDirectories).value.flatMap {
case dir if dir.getPath.endsWith("scala") => ltEqVersions.map { case (m, n) => file(dir.getPath + s"-$m.$n+") }
case _ => Seq.empty
}
},
Test / unmanagedSourceDirectories ++= {
val version = scalaVersion.value
val crossVersions = crossScalaVersions.value
import Ordering.Implicits._
val ltEqVersions = crossVersions.map(CrossVersion.partialVersion).filter(_ <= CrossVersion.partialVersion(version)).flatten
(Test / unmanagedSourceDirectories).value.flatMap {
case dir if dir.getPath.endsWith("scala") => ltEqVersions.map { case (m, n) => file(dir.getPath + s"-$m.$n+") }
case _ => Seq.empty
}
},
Compile / unmanagedSourceDirectories ++= {
val version = scalaVersion.value
val crossVersions = crossScalaVersions.value
import Ordering.Implicits._
val gtEqVersions = crossVersions.map(CrossVersion.partialVersion).filter(_ >= CrossVersion.partialVersion(version)).flatten
(Compile / unmanagedSourceDirectories).value.flatMap {
case dir if dir.getPath.endsWith("scala") => gtEqVersions.map { case (m, n) => file(dir.getPath + s"-$m.$n-") }
case _ => Seq.empty
}
},
Test / unmanagedSourceDirectories ++= {
val version = scalaVersion.value
val crossVersions = crossScalaVersions.value
import Ordering.Implicits._
val gtEqVersions = crossVersions.map(CrossVersion.partialVersion).filter(_ >= CrossVersion.partialVersion(version)).flatten
(Test / unmanagedSourceDirectories).value.flatMap {
case dir if dir.getPath.endsWith("scala") => gtEqVersions.map { case (m, n) => file(dir.getPath + s"-$m.$n-") }
case _ => Seq.empty
}
},
Compile / unmanagedSourceDirectories ++= {
val crossVersions = crossScalaVersions.value
import Ordering.Implicits._
val ltEqVersions = crossVersions.map(CrossVersion.partialVersion).sorted.flatten
def joinV = (_: Product).productIterator.mkString(".")
val allRangeVersions = (2 to math.max(2, ltEqVersions.size))
.flatMap(i => ltEqVersions.sliding(i).filter(_.size == i))
.map(l => (l.head, l.last))
.distinct
CrossVersion.partialVersion(scalaVersion.value).toList.flatMap {
version =>
val rangeVersions = allRangeVersions
.filter { case (l, r) => l <= version && version <= r }
.map { case (l, r) => s"-${joinV(l)}-${joinV(r)}" }
(Compile / unmanagedSourceDirectories).value.flatMap {
case dir if dir.getPath.endsWith("scala") => rangeVersions.map { vStr => file(dir.getPath + vStr) }
case _ => Seq.empty
}
}
},
Test / unmanagedSourceDirectories ++= {
val crossVersions = crossScalaVersions.value
import Ordering.Implicits._
val ltEqVersions = crossVersions.map(CrossVersion.partialVersion).sorted.flatten
def joinV = (_: Product).productIterator.mkString(".")
val allRangeVersions = (2 to math.max(2, ltEqVersions.size))
.flatMap(i => ltEqVersions.sliding(i).filter(_.size == i))
.map(l => (l.head, l.last))
.distinct
CrossVersion.partialVersion(scalaVersion.value).toList.flatMap {
version =>
val rangeVersions = allRangeVersions
.filter { case (l, r) => l <= version && version <= r }
.map { case (l, r) => s"-${joinV(l)}-${joinV(r)}" }
(Test / unmanagedSourceDirectories).value.flatMap {
case dir if dir.getPath.endsWith("scala") => rangeVersions.map { vStr => file(dir.getPath + vStr) }
case _ => Seq.empty
}
}
},
Compile / doc / sources := { (isSnapshot.value, scalaVersion.value) match {
case (_, "3.3.6") => Seq(
)
case (_, _) => (Compile / doc / sources).value
} },
Test / testOptions += Tests.Argument("-oDF"),
scalacOptions ++= { (isSnapshot.value, scalaVersion.value) match {
case (_, "2.11.12") => Seq.empty
case (_, "2.12.20") => Seq(
"-release:8",
"-explaintypes",
"-Ypartial-unification",
if (insideCI.value) "-Wconf:any:error" else "-Wconf:any:warning",
"-Wconf:cat=optimizer:warning",
"-Wconf:cat=other-match-analysis:error",
"-Ybackend-parallelism",
math.min(16, math.max(1, sys.runtime.availableProcessors() - 1)).toString,
"-Xlint:adapted-args",
"-Xlint:by-name-right-associative",
"-Xlint:constant",
"-Xlint:delayedinit-select",
"-Xlint:doc-detached",
"-Xlint:inaccessible",
"-Xlint:infer-any",
"-Xlint:missing-interpolator",
"-Xlint:nullary-override",
"-Xlint:nullary-unit",
"-Xlint:option-implicit",
"-Xlint:package-object-classes",
"-Xlint:poly-implicit-overload",
"-Xlint:private-shadow",
"-Xlint:stars-align",
"-Xlint:type-parameter-shadow",
"-Xlint:unsound-match",
"-opt-warnings:_",
"-Ywarn-extra-implicit",
"-Ywarn-unused:_",
"-Ywarn-adapted-args",
"-Ywarn-dead-code",
"-Ywarn-inaccessible",
"-Ywarn-infer-any",
"-Ywarn-nullary-override",
"-Ywarn-nullary-unit",
"-Ywarn-numeric-widen",
"-Ywarn-unused-import",
"-Ywarn-value-discard",
"-Ycache-plugin-class-loader:always",
"-Ycache-macro-class-loader:last-modified",
"-Wconf:msg=nowarn:silent"
)
case (_, "2.13.14") => Seq(
"-release:8",
"-explaintypes",
if (insideCI.value) "-Wconf:any:error" else "-Wconf:any:warning",
"-Wconf:cat=optimizer:warning",
"-Wconf:cat=other-match-analysis:error",
"-Vtype-diffs",
"-Ybackend-parallelism",
math.min(16, math.max(1, sys.runtime.availableProcessors() - 1)).toString,
"-Wdead-code",
"-Wextra-implicit",
"-Wnumeric-widen",
"-Woctal-literal",
"-Wvalue-discard",
"-Wunused:_",
"-Wmacros:default",
"-Ycache-plugin-class-loader:always",
"-Ycache-macro-class-loader:last-modified",
"-Wconf:msg=nowarn:silent"
)
case (_, _) => Seq(
"-Ykind-projector",
"-no-indent",
"-language:implicitConversions"
)
} },
scalacOptions -= "-Wconf:any:error",
mimaPreviousArtifacts := { (isSnapshot.value, scalaVersion.value) match {
case (_, "3.3.6") => Set(organization.value %% name.value % "2.2.5", organization.value %% name.value % "2.1.0")
case (_, _) => Set(organization.value %% name.value % "2.2.5", organization.value %% name.value % "2.1.0", organization.value %% name.value % "1.0.0")
} },
scalacOptions ++= { (isSnapshot.value, scalaVersion.value) match {
case (_, "2.13.14") => Seq(
"-Xlint:-implicit-recursion"
)
case (_, _) => Seq.empty
} },
scalacOptions ++= { (isSnapshot.value, scalaVersion.value) match {
case (false, "2.12.20") => Seq(
"-opt:l:inline",
"-opt-inline-from:izumi.reflect.**"
)
case (false, "2.13.14") => Seq(
"-opt:l:inline",
"-opt-inline-from:izumi.reflect.**"
)
case (_, _) => Seq.empty
} },
Compile / packageBin / packageOptions += Package.ManifestAttributes("Automatic-Module-Name" -> s"${organization.value.replaceAll("-",".")}.${moduleName.value.replaceAll("-",".")}")
)
.jvmSettings(
crossScalaVersions := Seq(
"3.3.6",
"2.13.14",
"2.12.20",
"2.11.12"
),
scalaVersion := crossScalaVersions.value.head
)
.jsSettings(
crossScalaVersions := Seq(
"3.3.6",
"2.13.14",
"2.12.20"
),
scalaVersion := crossScalaVersions.value.head,
coverageEnabled := false,
scalaJSLinkerConfig := scalaJSLinkerConfig.value.withModuleKind(ModuleKind.CommonJSModule)
)
.nativeSettings(
crossScalaVersions := Seq(
"3.3.6",
"2.13.14",
"2.12.20"
),
scalaVersion := crossScalaVersions.value.head,
coverageEnabled := false,
test := {},
Test / test := {}
)
lazy val `izumi-reflectJVM` = `izumi-reflect`.jvm
lazy val `izumi-reflectJS` = `izumi-reflect`.js
lazy val `izumi-reflectNative` = `izumi-reflect`.native
lazy val `izumi-reflect-aggregate` = (project in file(".agg/izumi-reflect-izumi-reflect-aggregate"))
.settings(
publish / skip := true,
crossScalaVersions := Nil
)
.aggregate(
`izumi-reflect-thirdparty-boopickle-shadedJVM`,
`izumi-reflect-thirdparty-boopickle-shadedJS`,
`izumi-reflect-thirdparty-boopickle-shadedNative`,
`izumi-reflectJVM`,
`izumi-reflectJS`,
`izumi-reflectNative`
)
lazy val `izumi-reflect-aggregate-jvm` = (project in file(".agg/izumi-reflect-izumi-reflect-aggregate-jvm"))
.settings(
publish / skip := true,
crossScalaVersions := Nil
)
.aggregate(
`izumi-reflect-thirdparty-boopickle-shadedJVM`,
`izumi-reflectJVM`
)
lazy val `izumi-reflect-aggregate-js` = (project in file(".agg/izumi-reflect-izumi-reflect-aggregate-js"))
.settings(
publish / skip := true,
crossScalaVersions := Nil
)
.aggregate(
`izumi-reflect-thirdparty-boopickle-shadedJS`,
`izumi-reflectJS`
)
lazy val `izumi-reflect-aggregate-native` = (project in file(".agg/izumi-reflect-izumi-reflect-aggregate-native"))
.settings(
publish / skip := true,
crossScalaVersions := Nil
)
.aggregate(
`izumi-reflect-thirdparty-boopickle-shadedNative`,
`izumi-reflectNative`
)
lazy val `izumi-reflect-root-jvm` = (project in file(".agg/.agg-jvm"))
.settings(
publish / skip := true,
crossScalaVersions := Seq(
"3.3.6",
"2.13.14",
"2.12.20",
"2.11.12"
),
scalaVersion := crossScalaVersions.value.head
)
.aggregate(
`izumi-reflect-aggregate-jvm`
)
lazy val `izumi-reflect-root-js` = (project in file(".agg/.agg-js"))
.settings(
publish / skip := true,
crossScalaVersions := Seq(
"3.3.6",
"2.13.14",
"2.12.20",
"2.11.12"
),
scalaVersion := crossScalaVersions.value.head
)
.aggregate(
`izumi-reflect-aggregate-js`
)
lazy val `izumi-reflect-root-native` = (project in file(".agg/.agg-native"))
.settings(
publish / skip := true,
crossScalaVersions := Seq(
"3.3.6",
"2.13.14",
"2.12.20",
"2.11.12"
),
scalaVersion := crossScalaVersions.value.head
)
.aggregate(
`izumi-reflect-aggregate-native`
)
lazy val `izumi-reflect-root` = (project in file("."))
.settings(
publish / skip := true,
Global / onChangedBuildSource := ReloadOnSourceChanges,
ThisBuild / publishMavenStyle := true,
ThisBuild / scalacOptions ++= Seq(
"-encoding",
"UTF-8",
"-feature",
"-unchecked",
"-deprecation",
"-language:higherKinds"
),
ThisBuild / javacOptions ++= Seq(
"-encoding",
"UTF-8",
"-source",
"1.8",
"-target",
"1.8",
"-deprecation",
"-parameters",
"-Xlint:all",
"-XDignore.symbol.file"
),
crossScalaVersions := Nil,
ThisBuild / organization := "dev.zio",
ThisBuild / publishTo := {
if (isSnapshot.value) {
Some(
"central-snapshots" at "https://central.sonatype.com/repository/maven-snapshots/"
)
} else {
localStaging.value
}
},
ThisBuild / credentials ++=
{
Seq(
Path.userHome / ".sbt" / "secrets" / "credentials.sonatype-zio-new.properties",
file(".") / ".secrets" / "credentials.sonatype-nexus.properties"
)
.filter(_.exists())
.map(Credentials.apply)
},
ThisBuild / homepage := Some(url("https://zio.dev")),
ThisBuild / licenses := Seq("Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0")),
ThisBuild / developers := List(
Developer(id = "jdegoes", name = "John De Goes", url = url("http://degoes.net"), email = "john@degoes.net"),
Developer(id = "7mind", name = "Septimal Mind", url = url("https://github.com/7mind"), email = "team@7mind.io"),
),
ThisBuild / scmInfo := Some(ScmInfo(url("https://github.com/zio/izumi-reflect"), "scm:git:https://github.com/zio/izumi-reflect.git")),
ThisBuild / mimaBinaryIssueFilters ++= Seq(
ProblemFilters.exclude[MissingClassProblem]("izumi.reflect.macrortti.LightTypeTag$ParsedLightTypeTag"),
ProblemFilters.exclude[MissingClassProblem]("izumi.reflect.macrortti.LightTypeTag$ParsedLightTypeTag110"),
ProblemFilters.exclude[MissingClassProblem]("izumi.reflect.macrortti.LightTypeTag$ParsedLightTypeTag210"),
ProblemFilters.exclude[MissingClassProblem]("izumi.reflect.macrortti.LightTypeTag$ParsedLightTypeTagM8"),
ProblemFilters.exclude[IncompatibleResultTypeProblem]("izumi.reflect.macrortti.LightTypeTagRef#FullReference._1"),
ProblemFilters.exclude[InheritedNewAbstractMethodProblem]("izumi.reflect.macrortti.LightTypeTagRef*"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LTTRenderables.r_LambdaParameterName"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTag.binaryFormatVersion"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagRef.repr"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LTTRenderables.r_Wildcard"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LTTRenderables.prefixSplitter"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagRef.longNameWithPrefix"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagRef.longNameInternalSymbol"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagRef#AppliedNamedReference.symName"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagRef#AppliedNamedReference.prefix"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagRef.scalaStyledName"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagRef.scalaStyledRepr"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.AnyTag.=:="),
ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.AnyTag.<:<"),
ProblemFilters.exclude[Problem]("izumi.reflect.TagMacro.*"),
ProblemFilters.exclude[Problem]("izumi.reflect.macrortti.LightTypeTagImpl.*"),
ProblemFilters.exclude[Problem]("izumi.reflect.macrortti.LightTypeTagImpl#*"),
ProblemFilters.exclude[Problem]("izumi.reflect.dottyreflection.*"),
ProblemFilters.exclude[Problem]("izumi.reflect.thirdparty.*"),
ProblemFilters.exclude[Problem]("izumi.reflect.internal.*"),
ProblemFilters.exclude[Problem]("izumi.reflect.ReflectionUtil*"),
ProblemFilters.exclude[DirectMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagImpl.norm"),
ProblemFilters.exclude[DirectMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagImpl.izumi$reflect$macrortti$LightTypeTagImpl$$*"),
ProblemFilters.exclude[DirectMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagInheritance.CtxExt"),
ProblemFilters.exclude[MissingFieldProblem]("izumi.reflect.macrortti.LightTypeTagInheritance.CtxExt"),
ProblemFilters.exclude[FinalClassProblem]("izumi.reflect.macrortti.LightTypeTagInheritance$CtxExt"),
ProblemFilters.exclude[MissingTypesProblem] ("izumi.reflect.macrortti.LightTypeTagInheritance$Ctx*"),
ProblemFilters.exclude[Problem] ("izumi.reflect.macrortti.LightTypeTagInheritance#Ctx*"),
ProblemFilters.exclude[Problem] ("izumi.reflect.macrortti.LightTypeTagUnpacker*")
),
ThisBuild / mimaFailOnProblem := true,
ThisBuild / mimaFailOnNoPrevious := false,
ThisBuild / useGpg := false,
libraryDependencies += "io.7mind.izumi.sbt" % "sbtgen_2.13" % "0.0.107" % Provided
)
.aggregate(
`izumi-reflect-aggregate`
)
================================================
FILE: docs/index.md
================================================
---
id: index
title: "Introduction to izumi-reflect"
sidebar_label: "izumi-reflect"
---
@PROJECT_BADGES@
================================================
FILE: docs/package.json
================================================
{
"name": "@zio.dev/izumi-reflect",
"description": "izumi-reflect Documentation",
"license": "Apache-2.0"
}
================================================
FILE: docs/sidebars.js
================================================
const sidebars = {
sidebar: [
{
type: "category",
label: "Izumi Reflect",
collapsed: false,
link: { type: "doc", id: "index" },
items: [ ]
}
]
};
module.exports = sidebars;
================================================
FILE: flake.nix
================================================
{
description = "izumi-reflect build environment";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/25.11";
inputs.flake-utils.url = "github:numtide/flake-utils";
inputs.mudyla.url = "github:7mind/mudyla";
inputs.mudyla.inputs.nixpkgs.follows = "nixpkgs";
outputs =
{ self
, nixpkgs
, flake-utils
, mudyla
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
in
{
devShells.default = pkgs.mkShell {
nativeBuildInputs = with pkgs.buildPackages; [
ncurses
coursier
sbt
nodejs
nodePackages.npm
gitMinimal
gnupg
mudyla.packages.${system}.default
];
shellHook = ''
export JDK11=${pkgs.jdk11_headless}
export JDK17=${pkgs.jdk17_headless}
export JDK21=${pkgs.jdk21_headless}
export JDK_DEV=${pkgs.graalvmPackages.graalvm-ce}
# Create .env directory with JDK symlink (ignore errors if already exists)
mkdir -p ./.env 2>/dev/null || true
rm -f ./.env/jdk 2>/dev/null || true
ln -sf ''${JDK_DEV} ./.env/jdk 2>/dev/null || true
'';
};
}
);
}
================================================
FILE: fmt-all
================================================
#!/usr/bin/env sh
cs launch scalafmt -- $@
================================================
FILE: izumi-reflect/izumi-reflect/.js/src/test/scala-2/izumi/reflect/test/PlatformSpecific.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.test
import izumi.reflect.macrortti.{LTag, LightTypeTag}
object PlatformSpecific {
def fromRuntime[T: LTag]: LightTypeTag = LTag[T].tag
def fromRuntime[T: LTag](loud: Boolean): LightTypeTag = LTag[T].tag
}
================================================
FILE: izumi-reflect/izumi-reflect/.jvm/src/test/scala-2/izumi/reflect/test/AllPartsStrongTestScala2Jvm.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.test
import izumi.reflect.ReflectionUtil
import izumi.reflect.test.DiscoveryModel.{DiscoverableService, DiscoverableServiceImpl, DiscoveryNodeProvider, GetDiscoveryNode}
import org.scalatest.wordspec.AnyWordSpec
class AllPartsStrongTestScala2Jvm extends AnyWordSpec {
type FP1[+T] = List[T]
type Ap1[+F[+_], +T] = F[T]
type FP[+T] = FP1[T]
trait C {
type A
}
"allPartsStrong for Identity typelambda" in {
val res1 = ReflectionUtil.allPartsStrong(scala.reflect.runtime.universe.typeOf[ID.id[C]].typeConstructor)
assert(res1)
}
"allPartsStrong for eta-expansion typelambda" in {
val res1 = ReflectionUtil.allPartsStrong(scala.reflect.runtime.universe.typeOf[FP1[C]].typeConstructor)
assert(res1)
}
"allPartsStrong for application typelambda" in {
val tpe = scala.reflect.runtime.universe.typeOf[Ap1[List, Int]].typeConstructor
val res1 = ReflectionUtil.allPartsStrong(tpe)
assert(res1)
}
"allPartsStrong for anonymous application typelambda" in {
val tpe = scala
.reflect.runtime.universe.weakTypeOf[{ type l[F[_], A] = F[A] }]
.asInstanceOf[scala.reflect.runtime.universe.RefinedTypeApi].decl(scala.reflect.runtime.universe.TypeName("l"))
.asType.typeSignature
.typeConstructor
val res1 = ReflectionUtil.allPartsStrong(tpe)
assert(res1)
}
"allPartsStrong for x.F[x.Id] typelambda" in {
val res1 = ReflectionUtil.allPartsStrong { object x { type F[_[_]]; type Id[A] = A }; scala.reflect.runtime.universe.weakTypeOf[x.F[x.Id]] }
assert(res1)
}
"allPartsStrong is false for TC#DiscoveryNode type projection" in {
def test1[TC <: DiscoverableService]: Boolean = {
ReflectionUtil.allPartsStrong(scala.reflect.runtime.universe.weakTypeOf[DiscoveryNodeProvider[GetDiscoveryNode[TC]]])
}
def test2[TC <: DiscoverableService]: Boolean = {
ReflectionUtil.allPartsStrong(scala.reflect.runtime.universe.weakTypeOf[DiscoveryNodeProvider[TC#DiscoveryNode]])
}
assert(!test1[DiscoverableServiceImpl])
assert(!test2[DiscoverableServiceImpl])
}
}
================================================
FILE: izumi-reflect/izumi-reflect/.jvm/src/test/scala-2/izumi/reflect/test/PlatformSpecific.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.test
import izumi.reflect.macrortti.{LightTypeTag, LightTypeTagImpl}
import scala.reflect.runtime.{universe => ru}
object PlatformSpecific {
/** Use for stepping through in debugger */
def fromRuntime[T: ru.TypeTag]: LightTypeTag = fromRuntime(ru.typeOf[T], loud = false)
def fromRuntime[T: ru.TypeTag](loud: Boolean): LightTypeTag = fromRuntime(ru.typeOf[T], loud)
/** Use for stepping through in debugger */
def fromRuntime(tpe: ru.Type, loud: Boolean): LightTypeTag = {
def makeTag() = LightTypeTagImpl.makeLightTypeTag(ru)(tpe)
synchronized {
if (loud) TagLogging.withSanityChecks(TagLogging.withDebugOutput(makeTag())) else makeTag()
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/.native/src/test/scala-2/izumi/reflect/test/PlatformSpecific.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.test
import izumi.reflect.macrortti.{LTag, LightTypeTag}
object PlatformSpecific {
def fromRuntime[T: LTag]: LightTypeTag = LTag[T].tag
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala/izumi/reflect/DebugProperties.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect
/**
* Java properties and macro settings that control behavior and debug output of Lightweight Reflection macros
*
* @see [[DebugProperties]]
*/
object DebugProperties {
/**
* Set system property `-Dizumi.reflect.rtti.optimized.equals=false` to disable optimized `equals` comparison for
* monomorphic [[izumi.reflect.macrortti.LightTypeTag]]s (instances of [[izumi.reflect.macrortti.LightTypeTag.ParsedLightTypeTag210]]).
* Try this if you're experiencing "impossible" false negatives. While we believe that such false negatives are impossible,
* even between tags generated by different versions of `izumi-reflect`, they could still be possible in actuality.
*
* {{{
* sbt -Dizumi.reflect.rtti.optimized.equals=false
* }}}
*
* Default: `true`
*/
final val `izumi.reflect.rtti.optimized.equals` = "izumi.reflect.rtti.optimized.equals"
/**
* Add compiler option `-Xmacro-settings:izumi.reflect.rtti.cache.compile=false` to disable compile-time caching of computed
* LightTypeTags. Caching is enabled by default for compile-time light type tag creation.
*
* {{{
* scalacOptions += "-Xmacro-settings:izumi.reflect.rtti.cache.compile=false"
* }}}
*
* Default: `true`
*/
final val `izumi.reflect.rtti.cache.compile` = "izumi.reflect.rtti.cache.compile"
/**
* Set system property `-Dizumi.reflect.rtti.cache.runtime=false` to disable caching for runtime creation of LightTypeTags.
* Caching is enabled by default for runtime light type tag creation.
*
* {{{
* sbt -Dizumi.reflect.rtti.cache.runtime=false
* }}}
*
* Default: `true`
*/
final val `izumi.reflect.rtti.cache.runtime` = "izumi.reflect.rtti.cache.runtime"
/**
* To see macro debug output during compilation, set `-Dizumi.reflect.debug.macro.rtti=true` system property
*
* {{{
* sbt -Dizumi.reflect.debug.macro.rtti=true compile
* }}}
*
* Default: `false`
*/
final val `izumi.reflect.debug.macro.rtti` = "izumi.reflect.debug.macro.rtti"
/**
* To enable sanity checking assertions during compilation, set `-Dizumi.reflect.debug.macro.rtti=true` system property
*
* {{{
* sbt -Dizumi.reflect.debug.macro.rtti.assertions=true compile
* }}}
*
* Default: `false`
*/
final val `izumi.reflect.debug.macro.rtti.assertions` = "izumi.reflect.debug.macro.rtti.assertions"
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala/izumi/reflect/internal/fundamentals/collections/IzCollections.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.internal.fundamentals.collections
import izumi.reflect.internal.CollectionCompat
import scala.language.implicitConversions
private[reflect] object IzCollections {
private[reflect] implicit def toRich[A, B](xs: CollectionCompat.IterableOnce[(A, B)]): IzMappings[A, B] = new IzMappings(xs)
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala/izumi/reflect/internal/fundamentals/collections/IzMappings.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.internal.fundamentals.collections
import izumi.reflect.internal.{CollectionCompat, NowarnCompat}
import scala.collection.mutable
private[reflect] final class IzMappings[A, B](private val list: CollectionCompat.IterableOnce[(A, B)]) extends AnyVal {
@NowarnCompat.nowarn("msg=deprecated")
def toMutableMultimap: MutableMultiMap[A, B] = {
list.foldLeft(new mutable.HashMap[A, mutable.Set[B]] with mutable.MultiMap[A, B]) {
(map, pair) => map.addBinding(pair._1, pair._2)
}
}
@NowarnCompat.nowarn("msg=deprecated")
def toMultimap: ImmutableMultiMap[A, B] = {
toMutableMultimap.mapValues(_.toSet).toMap
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala/izumi/reflect/internal/fundamentals/collections/package.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.internal.fundamentals
import izumi.reflect.internal.NowarnCompat
import scala.collection.mutable
package object collections {
@NowarnCompat.nowarn("msg=deprecated")
private[reflect] type MutableMultiMap[A, B] = mutable.HashMap[A, mutable.Set[B]] with mutable.MultiMap[A, B]
private[reflect] type ImmutableMultiMap[A, B] = Map[A, Set[B]]
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala/izumi/reflect/internal/fundamentals/functional/Renderable.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.internal.fundamentals.functional
import izumi.reflect.internal.fundamentals.functional.WithRenderableSyntax.RenderableSyntax
import scala.language.implicitConversions
private[reflect] trait Renderable[T] {
def render(value: T): String
}
private[reflect] object Renderable extends WithRenderableSyntax {
@inline def apply[T: Renderable]: Renderable[T] = implicitly
}
private[reflect] trait WithRenderableSyntax {
@inline implicit final def RenderableSyntax[T](r: T): RenderableSyntax[T] = new RenderableSyntax[T](r)
}
private[reflect] object WithRenderableSyntax {
private[reflect] final class RenderableSyntax[T](private val r: T) extends AnyVal {
def render()(implicit R: Renderable[T]): String = R.render(r)
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala/izumi/reflect/internal/fundamentals/platform/assertions/IzAssert.scala
================================================
package izumi.reflect.internal.fundamentals.platform.assertions
import izumi.reflect.DebugProperties
import izumi.reflect.internal.fundamentals.platform.strings.IzString.toRichString
import java.util.concurrent.atomic.AtomicBoolean
private[reflect] object IzAssert {
private[reflect] final def apply(assertion: => Boolean): Unit = apply(assertion, "")
private[reflect] final def apply(assertion: => Boolean, clue: => Any): Unit = {
if (statusAsserts()) {
if (!assertion) throw new IllegalArgumentException(s"IzAssert failed: $clue")
}
}
/** caching is enabled by default for runtime light type tag creation */
private[this] val enabled: AtomicBoolean = {
val prop = System.getProperty(DebugProperties.`izumi.reflect.debug.macro.rtti.assertions`).asBoolean().getOrElse(false)
new AtomicBoolean(prop)
}
// for calling within a debugger or tests
private[reflect] def enableAsserts(): Unit = enabled.set(true)
private[reflect] def disableAsserts(): Unit = enabled.set(false)
private[reflect] def statusAsserts(): Boolean = enabled.get()
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala/izumi/reflect/internal/fundamentals/platform/basics/IzBoolean.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.internal.fundamentals.platform.basics
import scala.language.implicitConversions
private[reflect] object IzBoolean {
@inline private[reflect] final implicit class LazyBool(private val b: () => Boolean) extends AnyVal {
@inline def value: Boolean = b()
}
@inline implicit final def toLazyBool(b: => Boolean): LazyBool = new LazyBool(() => b)
@inline final def all(b1: Boolean, b2: => Boolean): Boolean = {
b1 && b2
}
@inline final def all(b1: Boolean, b: LazyBool*): Boolean = {
b1 && b.forall(_.value)
}
@inline final def any(b1: Boolean): Boolean = {
b1
}
@inline final def any(b1: Boolean, b2: => Boolean): Boolean = {
b1 || b2
}
@inline final def any(b1: Boolean, b2: => Boolean, b3: => Boolean): Boolean = {
b1 || b2 || b3
}
@inline final def any(b1: Boolean, b2: => Boolean, b3: => Boolean, b4: => Boolean): Boolean = {
b1 || b2 || b3 || b4
}
@inline final def any(b1: Boolean, b: LazyBool*): Boolean = {
b1 || b.exists(_.value)
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala/izumi/reflect/internal/fundamentals/platform/console/TrivialLogger.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.internal.fundamentals.platform.console
import izumi.reflect.DebugProperties
import izumi.reflect.internal.NowarnCompat
import izumi.reflect.internal.fundamentals.platform.console.TrivialLogger.Config
import izumi.reflect.internal.fundamentals.platform.strings.IzString._
import java.util.concurrent.atomic.AtomicBoolean
import scala.annotation.tailrec
import scala.reflect.{ClassTag, classTag}
private[reflect] trait TrivialLogger {
def log(s: => String): Unit
def sub(): TrivialLogger = sub(1)
def sub(delta: Int): TrivialLogger
}
private[reflect] trait AbstractStringTrivialSink {
def flush(value: => String): Unit
}
private[reflect] object AbstractStringTrivialSink {
private[reflect] object Console extends AbstractStringTrivialSink {
override def flush(value: => String): Unit = System.out.println(value)
}
}
private[reflect] final class TrivialLoggerImpl(config: Config, id: String, logMessages: Boolean, loggerLevel: Int) extends TrivialLogger {
override def log(s: => String): Unit = {
flush(format(s))
}
override def sub(delta: Int): TrivialLogger = {
new TrivialLoggerImpl(config, id, logMessages, loggerLevel + delta)
}
@inline private[this] def format(s: => String): String = {
s"$id: $s"
}
@inline private[this] def flush(s: => String): Unit = {
if (logMessages) {
config.sink.flush(s.shift(loggerLevel, "> "))
}
}
}
private[reflect] object TrivialLogger {
private[reflect] final case class Config(
sink: AbstractStringTrivialSink,
forceLog: Boolean
)
private[reflect] object Config {
private[reflect] lazy val console: Config = Config(sink = AbstractStringTrivialSink.Console, forceLog = false)
}
def make[T: ClassTag](config: Config): TrivialLogger = {
val logMessages: Boolean = checkLog(config)
new TrivialLoggerImpl(config, classTag[T].runtimeClass.getSimpleName, logMessages, loggerLevel = 0)
}
@inline private[this] def checkLog(config: Config): Boolean = {
config.forceLog || enabled.get()
}
@NowarnCompat.nowarn("msg=return statement")
private[this] val enabled: AtomicBoolean = {
def prop(): Boolean = {
val sysProperty = DebugProperties.`izumi.reflect.debug.macro.rtti` // this is the only debug logging property supported in the library
val default = false
val parts = sysProperty.split('.').toList
@tailrec
def check(current: String, tail: List[String]): Boolean = {
if (System.getProperty(current).asBoolean().getOrElse(default)) {
true
} else {
tail match {
case ::(head, next) => check(s"$current.$head", next)
case Nil => default
}
}
}
check(parts.head, parts.tail)
}
new AtomicBoolean(prop())
}
// for calling within a debugger or tests
private[reflect] def enableLogs(): Unit = enabled.set(true)
private[reflect] def disableLogs(): Unit = enabled.set(false)
private[reflect] def statusLogs(): Boolean = enabled.get()
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala/izumi/reflect/internal/fundamentals/platform/strings/IzString.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.internal.fundamentals.platform.strings
import scala.language.implicitConversions
import scala.util.Try
private[reflect] final class IzString(private val s: String) extends AnyVal {
@inline final def asBoolean(): Option[Boolean] = {
Try(s.toBoolean).toOption
}
@inline final def shift(delta: Int, fill: String = " "): String = {
val shift = fill * delta
s.split("\\\n", -1).map(s => s"$shift$s").mkString("\n")
}
}
private[reflect] final class IzIterable[A](private val s: Iterable[A]) extends AnyVal {
def niceList(shift: String = " ", prefix: String = "- "): String = {
if (s.nonEmpty) {
val fullPrefix = s"\n$shift$prefix"
s.mkString(fullPrefix, fullPrefix, "")
} else {
"ø"
}
}
}
private[reflect] object IzString {
private[reflect] implicit def toRichString(s: String): IzString = new IzString(s)
private[reflect] implicit def toRichIterable[A](s: Iterable[A]): IzIterable[A] = new IzIterable(s)
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala/izumi/reflect/macrortti/LTTOrdering.scala
================================================
package izumi.reflect.macrortti
import izumi.reflect.internal.OrderingCompat
import izumi.reflect.macrortti.LightTypeTagRef.SymName.{LambdaParamName, SymLiteral, SymTermName, SymTypeName}
import scala.util.Sorting
private[macrortti] trait LTTOrdering {
import LightTypeTagRef._
@inline private[macrortti] final def OrderingAbstractReferenceInstance[A <: AbstractReference]: Ordering[A] = OrderingAbstractReference.asInstanceOf[Ordering[A]]
@inline private[macrortti] final def OrderingRefinementDeclInstance: Ordering[RefinementDecl] = OrderingRefinementDecl
private[macrortti] def refSetToSortedArray[T <: AbstractReference](set: Set[_ <: T]): Array[T] = {
@inline implicit def OrderingInstance: Ordering[AbstractReference] = LightTypeTagRef.OrderingAbstractReferenceInstance
val array: Array[AbstractReference] = set.toArray
Sorting.stableSort(array)
array.asInstanceOf[Array[T]]
}
private[macrortti] def refinementDeclSetToSortedArray(set: Set[RefinementDecl]): Array[RefinementDecl] = {
@inline implicit def OrderingInstance: Ordering[RefinementDecl] = LightTypeTagRef.OrderingRefinementDeclInstance
val array: Array[RefinementDecl] = set.toArray
Sorting.stableSort(array)
array
}
private[this] val OrderingAbstractReference: Ordering[AbstractReference] = new Ordering[AbstractReference] {
override def equiv(x: AbstractReference, y: AbstractReference): Boolean = x == y
override def compare(x: AbstractReference, y: AbstractReference): Int = (x, y) match {
case (lx: Lambda, ly: Lambda) =>
// Mirror Lambda#equals
val compare1 = Ordering.Int.compare(lx.input.size, ly.input.size)
if (compare1 != 0) return compare1
OrderingAbstractReference.compare(lx.normalizedOutput, ly.normalizedOutput)
case (IntersectionReference(refsx), IntersectionReference(refsy)) =>
OrderingArrayAbstractReference.compare(refSetToSortedArray(refsx), refSetToSortedArray(refsy))
case (UnionReference(refsx), UnionReference(refsy)) =>
OrderingArrayAbstractReference.compare(refSetToSortedArray(refsx), refSetToSortedArray(refsy))
case (Refinement(referencex, declsx), Refinement(referencey, declsy)) =>
val compare1 = compare(referencex, referencey)
if (compare1 != 0) return compare1
OrderingArrayRefinementDecl.compare(refinementDeclSetToSortedArray(declsx), refinementDeclSetToSortedArray(declsy))
case (NameReference(symx, boundariesx, prefixx), NameReference(symy, boundariesy, prefixy)) =>
val compare1 = OrderingSymName.compare(symx, symy)
if (compare1 != 0) return compare1
val compare2 = OrderingBoundaries.compare(boundariesx, boundariesy)
if (compare2 != 0) return compare2
OrderingOptionAbstractReference.compare(prefixx, prefixy)
case (FullReference(refx, parametersx, prefixx), FullReference(refy, parametersy, prefixy)) =>
val compare1 = OrderingSymName.compare(refx, refy)
if (compare1 != 0) return compare1
val compare2 = OrderingListTypeParam.compare(parametersx, parametersy)
if (compare2 != 0) return compare2
OrderingOptionAbstractReference.compare(prefixx, prefixy)
case _ =>
def idx(abstractReference: AbstractReference): Int = abstractReference match {
case _: Lambda => 0
case _: IntersectionReference => 1
case _: UnionReference => 2
case _: Refinement => 3
case _: NameReference => 4
case _: FullReference => 5
case _: WildcardReference => 6
}
Ordering.Int.compare(idx(x), idx(y))
}
}
private[this] val OrderingRefinementDecl: Ordering[RefinementDecl] = new Ordering[RefinementDecl] {
override def equiv(x: RefinementDecl, y: RefinementDecl): Boolean = x == y
override def compare(x: RefinementDecl, y: RefinementDecl): Int = (x, y) match {
case (RefinementDecl.Signature(namex, inputx, outputx), RefinementDecl.Signature(namey, inputy, outputy)) =>
val compare1 = Ordering.String.compare(namex, namey)
if (compare1 != 0) return compare1
val compare2 = OrderingListAbstractReference.compare(inputx, inputy)
if (compare2 != 0) return compare2
OrderingAbstractReference.compare(outputx, outputy)
case (RefinementDecl.TypeMember(namex, refx), RefinementDecl.TypeMember(namey, refy)) =>
val compare1 = Ordering.String.compare(namex, namey)
if (compare1 != 0) return compare1
OrderingAbstractReference.compare(refx, refy)
case _ =>
def idx(refinementDecl: RefinementDecl): Int = refinementDecl match {
case _: RefinementDecl.Signature => 0
case _: RefinementDecl.TypeMember => 1
}
Ordering.Int.compare(idx(x), idx(y))
}
}
private[this] val OrderingSymName: Ordering[SymName] = new Ordering[SymName] {
override def equiv(x: SymName, y: SymName): Boolean = x == y
override def compare(x: SymName, y: SymName): Int = {
def idx(symName: SymName): Int = symName match {
case SymTermName(_) => 0
case SymTypeName(_) => 1
case SymLiteral(_) => 2
case LambdaParamName(_, _, _) => 3
}
val compare1 = Ordering.Int.compare(idx(x), idx(y))
if (compare1 != 0) return compare1
(x, y) match {
case (x1: SymName.NamedSymbol, y1: SymName.NamedSymbol) => Ordering.String.compare(x1.name, y1.name)
case (x1: SymName.LambdaParamName, y1: SymName.LambdaParamName) =>
Ordering.Tuple3[Int, Int, Int].compare((x1.depth, x1.index, x1.arity), (y1.depth, y1.index, y1.arity))
case (x1: SymName.NamedSymbol, y1: SymName.LambdaParamName) =>
Ordering.String.compare(x1.name, SymName.forceName(y1))
case (x1: SymName.LambdaParamName, y1: SymName.NamedSymbol) =>
Ordering.String.compare(SymName.forceName(x1), y1.name)
}
}
}
private[this] val OrderingBoundaries: Ordering[Boundaries] = new Ordering[Boundaries] {
override def equiv(x: Boundaries, y: Boundaries): Boolean = x == y
override def compare(x: Boundaries, y: Boundaries): Int = (x, y) match {
case (Boundaries.Defined(rebx, retx), Boundaries.Defined(reby, rety)) =>
val compare1 = OrderingAbstractReference.compare(rebx, reby)
if (compare1 != 0) return compare1
OrderingAbstractReference.compare(retx, rety)
case (x, y) =>
def idx(boundaries: Boundaries): Int = boundaries match {
case _: Boundaries.Empty.type => 0
case _: Boundaries.Defined => 1
}
Ordering.Int.compare(idx(x), idx(y))
}
}
private[this] val OrderingTypeParam: Ordering[TypeParam] = new Ordering[TypeParam] {
override def equiv(x: TypeParam, y: TypeParam): Boolean = x == y
override def compare(x: TypeParam, y: TypeParam): Int = (x, y) match {
case (TypeParam(namex, varx), TypeParam(namey, vary)) =>
val compare1 = OrderingAbstractReference.compare(namex, namey)
if (compare1 != 0) return compare1
OrderingVariance.compare(varx, vary)
}
}
private[this] val OrderingVariance: Ordering[Variance] = Ordering.by {
case Variance.Invariant => 0
case Variance.Contravariant => 1
case Variance.Covariant => 2
}
private[this] val OrderingListAbstractReference: Ordering[List[AbstractReference]] = OrderingCompat.listOrdering(OrderingAbstractReference)
private[this] val OrderingArrayAbstractReference: Ordering[Array[AbstractReference]] = OrderingCompat.arrayOrdering(OrderingAbstractReference)
private[this] val OrderingOptionAbstractReference: Ordering[Option[AbstractReference]] = Ordering.Option(OrderingAbstractReference)
private[this] val OrderingArrayRefinementDecl: Ordering[Array[RefinementDecl]] = OrderingCompat.arrayOrdering(OrderingRefinementDecl)
private[this] val OrderingListTypeParam: Ordering[List[TypeParam]] = OrderingCompat.listOrdering(OrderingTypeParam)
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala/izumi/reflect/macrortti/LTTRenderables.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.macrortti
import izumi.reflect.internal.fundamentals.functional.{Renderable, WithRenderableSyntax}
import izumi.reflect.internal.fundamentals.platform.language.unused
import izumi.reflect.macrortti.LightTypeTagInheritance.{tpeAny, tpeAnyRef, tpeNothing, tpeNull, tpeObject}
import izumi.reflect.macrortti.LightTypeTagRef.SymName.SymLiteral
import izumi.reflect.macrortti.LightTypeTagRef._
trait LTTRenderables extends Serializable with WithRenderableSyntax {
def r_SymName(sym: SymName, hasPrefix: Boolean): String
def prefixSplitter: String = "::"
implicit lazy val r_LightTypeTag: Renderable[LightTypeTagRef] = new Renderable[LightTypeTagRef] {
override def render(value: LightTypeTagRef): String = value match {
case a: AbstractReference =>
a.render()
}
}
implicit lazy val r_AbstractReference: Renderable[AbstractReference] = new Renderable[AbstractReference] {
override def render(value: AbstractReference): String = value match {
case a: AppliedReference =>
a.render()
case l: Lambda =>
l.render()
}
}
implicit lazy val r_AppliedReference: Renderable[AppliedReference] = new Renderable[AppliedReference] {
override def render(value: AppliedReference): String = value match {
case a: AppliedNamedReference =>
a.render()
case i: IntersectionReference =>
i.render()
case u: UnionReference =>
u.render()
case r: Refinement =>
r.render()
case r: WildcardReference =>
r.render()
}
}
implicit lazy val r_Refinement: Renderable[Refinement] = new Renderable[Refinement] {
override def render(value: Refinement): String = {
s"(${value.reference.render()} ${value.decls.toSeq.sorted(OrderingRefinementDeclInstance).map(_.render()).mkString("{", ", ", "}")})"
}
}
implicit lazy val r_Wildcard: Renderable[WildcardReference] = new Renderable[WildcardReference] {
override def render(value: WildcardReference): String = {
value.boundaries match {
case _: Boundaries.Defined =>
s"?: ${value.boundaries.render()}"
case Boundaries.Empty =>
"?"
}
}
}
implicit lazy val r_RefinementDecl: Renderable[RefinementDecl] = new Renderable[RefinementDecl] {
override def render(value: RefinementDecl): String = value match {
case RefinementDecl.Signature(name, input, output) =>
s"def $name${input.map(_.render()).mkString("(", ", ", ")")}: ${output.render()}"
case RefinementDecl.TypeMember(name, tpe) =>
s"type $name = $tpe"
}
}
implicit lazy val r_AppliedNamedReference: Renderable[AppliedNamedReference] = new Renderable[AppliedNamedReference] {
override def render(value: AppliedNamedReference): String = value match {
case n: NameReference =>
n.render()
case f: FullReference =>
f.render()
}
}
implicit lazy val r_Lambda: Renderable[Lambda] = new Renderable[Lambda] {
override def render(value: Lambda): String = {
s"λ ${value.input.map(_.render()).map(p => s"%$p").mkString(",")} → ${value.output.render()}"
}
}
implicit lazy val r_LambdaParameterName: Renderable[SymName.LambdaParamName] = new Renderable[SymName.LambdaParamName] {
override def render(value: SymName.LambdaParamName): String = {
value.depth match {
case t if t <= 0 =>
s"${value.index}"
case t if t > 0 =>
s"${value.depth}:${value.index}"
// FIXME so-called "debug" view doesn't display all the data here which could lead to confusion when "debugging"
// s"[${value.arity}]${value.depth}:${value.index}"
}
}
}
implicit lazy val r_NameRefRenderer: Renderable[NameReference] = new Renderable[NameReference] {
override def render(value: NameReference): String = {
val r = r_SymName(value.ref, value.prefix.isDefined)
val rr = value.boundaries match {
case _: Boundaries.Defined =>
s"$r|${value.boundaries.render()}"
case Boundaries.Empty =>
r
}
value.prefix match {
case Some(p) =>
s"${p.render()}$prefixSplitter$rr"
case None =>
rr
}
}
}
implicit lazy val r_FullReference: Renderable[FullReference] = new Renderable[FullReference] {
override def render(value: FullReference): String = {
s"${value.asName.render()}${value.parameters.map(_.render()).mkString("[", ",", "]")}"
}
}
implicit lazy val r_IntersectionReference: Renderable[IntersectionReference] = new Renderable[IntersectionReference] {
override def render(value: IntersectionReference): String = {
value.refs.toSeq.sorted(OrderingAbstractReferenceInstance).map(r => (r: AppliedReference).render()).mkString("{", " & ", "}")
}
}
implicit lazy val r_UnionReference: Renderable[UnionReference] = new Renderable[UnionReference] {
override def render(value: UnionReference): String = {
value.refs.toSeq.sorted(OrderingAbstractReferenceInstance).map(r => (r: AppliedReference).render()).mkString("{", " | ", "}")
}
}
implicit lazy val r_TypeParam: Renderable[TypeParam] = new Renderable[TypeParam] {
override def render(value: TypeParam): String = {
s"${value.variance.render()}${value.ref.render()}"
}
}
implicit lazy val r_Variance: Renderable[Variance] = new Renderable[Variance] {
override def render(value: Variance): String = value match {
case Variance.Invariant => "="
case Variance.Contravariant => "-"
case Variance.Covariant => "+"
}
}
implicit lazy val r_Boundaries: Renderable[Boundaries] = new Renderable[Boundaries] {
override def render(value: Boundaries): String = value match {
case Boundaries.Defined(bottom, top) =>
s"<${bottom.render()}..${top.render()}>"
case Boundaries.Empty =>
""
}
}
@deprecated("bincompat only", "20.03.2023")
private[macrortti] implicit lazy val r_LambdaParameter: Renderable[LambdaParameter] = new Renderable[LambdaParameter] {
override def render(value: LambdaParameter): String = value match {
case l: SymName.LambdaParamName => r_SymName(l, hasPrefix = false)
}
}
}
object LTTRenderables {
// omit package names
object Short extends LTTRenderables {
override def r_SymName(sym: SymName, @unused hasPrefix: Boolean): String = {
sym match {
case SymLiteral(c) =>
c
case t: SymName.LambdaParamName =>
t.render()
case s: SymName.NamedSymbol =>
s.name.split('.').last
}
}
}
object Long extends Long
// print package names
private[LTTRenderables] trait Long extends LTTRenderables {
override def r_SymName(sym: SymName, hasPrefix: Boolean): String = {
if (hasPrefix) {
Short.r_SymName(sym, hasPrefix)
} else {
sym match {
case t: SymName.LambdaParamName => t.render()
case o: SymName.NamedSymbol => o.name
}
}
}
private[macrortti] def renderDb(db: Map[_ <: AbstractReference, Set[_ <: AbstractReference]]): String = {
import izumi.reflect.internal.fundamentals.platform.strings.IzString._
db.toList.sortBy(_._1)(OrderingAbstractReferenceInstance).map {
case (k, v) => s"${k.repr} -> ${v.toList.sorted(OrderingAbstractReferenceInstance).map(_.repr).niceList(prefix = "* ").shift(2)}"
}.niceList()
}
}
// Same as `Long`, but split prefixes with . instead of ::
object LongPrefixDot extends LTTRenderables {
override def r_SymName(sym: SymName, hasPrefix: Boolean): String = {
Long.r_SymName(sym, hasPrefix)
}
override def prefixSplitter: String = "."
}
object ScalaStyledLambdas extends ScalaStyledLambdasShared {
override implicit lazy val r_LambdaParameterName: Renderable[SymName.LambdaParamName] = new Renderable[SymName.LambdaParamName] {
override def render(value: SymName.LambdaParamName): String = "_"
}
override implicit lazy val r_Lambda: Renderable[Lambda] = new Renderable[Lambda] {
override def render(value: Lambda): String = {
val isSimpleShape = value match {
case Lambda(input, FullReference(_, tparams, _)) =>
val (unusedRemaining, suspicious) = tparams.foldLeft((input, List.empty[AbstractReference])) {
// params must be applied in definition order
case ((all @ unusedArg :: tl, suspicious), TypeParam(p: AppliedNamedReference, variance)) =>
if (p.symName == unusedArg) {
p match {
case _: NameReference =>
(tl, suspicious)
case FullReference(symName, parameters, prefix) =>
(tl, parameters.map(_.ref) ++ suspicious)
}
} else {
(all, p :: suspicious)
}
case ((all, suspicious), TypeParam(other, _)) => (all, other :: suspicious)
}
val innerUses = suspicious.iterator.flatMap(RuntimeAPI.unpack).map(_.symName).toSet
unusedRemaining.isEmpty && input.toSet.intersect(innerUses).isEmpty
case _ =>
false
}
if (isSimpleShape) {
s"${value.output.render()}"
} else {
ScalaStyledLambdasLong.r_Lambda.render(value)
}
}
}
}
object ScalaStyledLambdasLong extends ScalaStyledLambdasShared {
override implicit lazy val r_LambdaParameterName: Renderable[SymName.LambdaParamName] = new Renderable[SymName.LambdaParamName] {
override def render(value: SymName.LambdaParamName): String = {
val char = ('A' + (value.index % 25)).toChar.toString
val numChars = 1 + value.index / 25
val suffix = if (value.depth > 0) s"${value.depth}" else ""
s"${char * numChars}$suffix"
}
}
override implicit lazy val r_Lambda: Renderable[Lambda] = new Renderable[Lambda] {
override def render(value: Lambda): String = {
s"[${value.input.map(_.render()).mkString(",")}] ➾ ${value.output.render()}"
}
}
}
private[LTTRenderables] trait ScalaStyledLambdasShared extends Long {
override def prefixSplitter: String = "."
override implicit lazy val r_Variance: Renderable[Variance] = new Renderable[Variance] {
override def render(value: Variance): String = value match {
case Variance.Invariant => ""
case Variance.Contravariant => "-"
case Variance.Covariant => "+"
}
}
override implicit lazy val r_NameRefRenderer: Renderable[NameReference] = new Renderable[NameReference] {
override def render(value: NameReference): String = {
val r = r_SymName(value.ref, value.prefix.isDefined)
val rr = value.boundaries match {
case _: Boundaries.Defined =>
s"$r ${value.boundaries.render()}"
case Boundaries.Empty =>
r
}
value.prefix match {
case Some(p) =>
s"${p.render()}$prefixSplitter$rr"
case None =>
rr
}
}
}
override implicit lazy val r_Boundaries: Renderable[Boundaries] = new Renderable[Boundaries] {
override def render(value: Boundaries): String = value match {
case Boundaries.Defined(bottom, top) if bottom == tpeNothing || bottom == tpeNull => s"<: ${top.render()}"
case Boundaries.Defined(bottom, top) if top == tpeAny || top == tpeAnyRef || top == tpeObject => s">: ${bottom.render()}"
case Boundaries.Defined(bottom, top) => s">: ${bottom.render()} <: ${top.render()}"
case Boundaries.Empty => ""
}
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala/izumi/reflect/macrortti/LTTSyntax.scala
================================================
package izumi.reflect.macrortti
import izumi.reflect.macrortti.LightTypeTagRef.SymName.LambdaParamName
import izumi.reflect.macrortti.LightTypeTagRef._
import scala.annotation.tailrec
private[macrortti] trait LTTSyntax {
this: LightTypeTagRef =>
protected[this] final def combineImpl(args: Seq[LightTypeTagRef]): AbstractReference = {
if (args.nonEmpty) {
applySeq(args.map { case v: AbstractReference => v })
} else {
// while user is not expected to combine an arbitrary tag with an empty args list
// it's a sound operation which should just return the tag itself
// see also: https://github.com/7mind/izumi/pull/1528
this match {
case ref: AbstractReference =>
ref
}
}
}
protected[this] final def combineNonPosImpl(args: Seq[Option[LightTypeTagRef]]): AbstractReference = {
applyParameters {
l =>
l.input.zip(args).flatMap {
case (p, v) =>
v match {
case Some(value: AbstractReference) =>
Seq(p -> value)
case None =>
Seq.empty
}
}
}
}
protected[this] final def withoutArgsImpl: AbstractReference = {
def appliedNamedReference(reference: AppliedNamedReference) = {
reference match {
case LightTypeTagRef.NameReference(_, _, _) => reference
case r @ LightTypeTagRef.FullReference(_, parameters @ _, prefix) => NameReference(r.symName, Boundaries.Empty, prefix)
}
}
def appliedReference(reference: AppliedReference): AppliedReference = {
reference match {
case reference: AppliedNamedReference =>
appliedNamedReference(reference)
case LightTypeTagRef.IntersectionReference(refs) =>
LightTypeTagRef.maybeIntersection(refs.map(appliedReference))
case LightTypeTagRef.UnionReference(refs) =>
LightTypeTagRef.maybeUnion(refs.map(appliedReference))
case LightTypeTagRef.Refinement(reference, decls) =>
LightTypeTagRef.Refinement(appliedReference(reference), decls)
case r: LightTypeTagRef.WildcardReference =>
r
}
}
@tailrec
def go(self: LightTypeTagRef): AbstractReference = {
self match {
case Lambda(_, output) =>
go(output)
case reference: AppliedReference =>
appliedReference(reference)
}
}
go(this)
}
/** Render to string, omitting package names */
protected[this] final def toStringImpl: String = {
import izumi.reflect.macrortti.LTTRenderables.Short._
(this: LightTypeTagRef).render()
}
/** Fully-qualified rendering of a type, including packages and prefix types.
* Use [[toString]] for a rendering that omits package names
*/
protected[this] final def reprImpl: String = {
import izumi.reflect.macrortti.LTTRenderables.Long._
(this: LightTypeTagRef).render()
}
protected[this] final def scalaStyledReprImpl: String = {
import izumi.reflect.macrortti.LTTRenderables.ScalaStyledLambdas._
(this: LightTypeTagRef).render()
}
protected[this] final def shortNameImpl: String = {
getName(r => LTTRenderables.Short.r_SymName(r.symName, hasPrefix = false))
}
protected[this] final def longNameWithPrefixImpl: String = {
getName(r => LTTRenderables.LongPrefixDot.r_NameRefRenderer.render(r.asName.copy(boundaries = Boundaries.Empty)))
}
protected[this] final def longNameInternalSymbolImpl: String = {
getName(r => LTTRenderables.Long.r_SymName(r.symName, hasPrefix = false))
}
@deprecated(
"Produces Scala version dependent output, with incorrect prefixes for types with value prefixes. Use `longNameWithPrefix` instead, or `longNameInternalSymbol` for old behavior",
"2.2.2"
)
protected[this] final def longNameImpl: String = {
longNameInternalSymbol
}
protected[this] final def getPrefixImpl: Option[AppliedReference] = {
@tailrec
@inline
def getPrefix(self: LightTypeTagRef): Option[AppliedReference] = {
self match {
case Lambda(_, output) => getPrefix(output)
case NameReference(_, _, prefix) => prefix
case FullReference(_, _, prefix) => prefix
case IntersectionReference(refs) =>
val prefixes = refs.flatMap(_.getPrefix)
if (prefixes.nonEmpty) Some(maybeIntersection(prefixes)) else None
case UnionReference(refs) =>
val prefixes = refs.flatMap(_.getPrefix)
if (prefixes.nonEmpty) Some(maybeUnion(prefixes)) else None
case Refinement(reference, _) => getPrefix(reference)
case _: WildcardReference => None
}
}
getPrefix(this)
}
protected[this] final def typeArgsImpl: List[AbstractReference] = {
this match {
case Lambda(input, output) =>
val params = input.iterator.toSet[SymName]
output.typeArgs.filter {
case n: AppliedNamedReference =>
!params.contains(n.asName.ref)
case _ =>
true
}
case NameReference(_, _, _) =>
Nil
case FullReference(_, parameters, _) =>
parameters.map(_.ref)
case IntersectionReference(_) =>
Nil
case UnionReference(_) =>
Nil
case WildcardReference(_) =>
Nil
case Refinement(reference, _) =>
reference.typeArgs
}
}
/** decompose intersection type */
protected[this] final def decomposeImpl: Set[AppliedReferenceExceptIntersection] = {
this match {
case IntersectionReference(refs) =>
refs.flatMap(_.decompose)
case appliedReference: AppliedReferenceExceptIntersection =>
Set(appliedReference)
// lambdas cannot appear _inside_ intersections in LightTypeTagRef model
case Lambda(_, _) =>
Set.empty
}
}
protected[this] final def decomposeUnionImpl: Set[AppliedReferenceExceptUnion] = {
this match {
case UnionReference(refs) =>
refs.flatMap(_.decomposeUnion)
case appliedReference: AppliedReferenceExceptUnion =>
Set(appliedReference)
// lambdas cannot appear _inside_ unions in LightTypeTagRef model
case Lambda(_, _) =>
Set.empty
}
}
private[macrortti] final def applySeq(refs: Seq[AbstractReference]): AbstractReference = {
applyParameters {
l =>
l.input.zip(refs).map {
case (p, v) =>
p -> v
}
}
}
private[macrortti] final def applyParameters(p: Lambda => Seq[(LambdaParamName, AbstractReference)]): AbstractReference = {
this match {
case l: Lambda =>
val parameters = p(l)
if (l.input.size < parameters.size) {
throw new IllegalArgumentException(s"$this expects no more than ${l.input.size} parameters: ${l.input} but got $parameters")
}
val expected = l.input.iterator.toSet
val unknownKeys = parameters.iterator.map(_._1).toSet.diff(expected)
if (unknownKeys.nonEmpty) {
throw new IllegalArgumentException(s"$this takes parameters: $expected but got unexpected ones: $unknownKeys")
}
RuntimeAPI.applyLambda(l, parameters)
case _ =>
throw new IllegalArgumentException(s"$this is not a type lambda, it cannot be parameterized")
}
}
@inline
private[this] final def getName(render: AppliedNamedReference => String): String = {
@tailrec
@inline
def go(r: LightTypeTagRef): String = r match {
case Lambda(_, output) => go(output)
case ref: NameReference => render(ref)
case ref: FullReference => render(ref)
case IntersectionReference(refs) => refs.map(goDeep).mkString(" & ")
case UnionReference(refs) => refs.map(goDeep).mkString(" | ")
case Refinement(reference, _) => go(reference)
case WildcardReference(_) => "?"
}
def goDeep(r: LightTypeTagRef): String = go(r)
go(this)
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala/izumi/reflect/macrortti/LightTypeTag.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.macrortti
import izumi.reflect.DebugProperties
import izumi.reflect.internal.NowarnCompat
import izumi.reflect.internal.OrderingCompat.ArraySeqLike
import izumi.reflect.internal.fundamentals.platform.strings.IzString.toRichString
import izumi.reflect.macrortti.LightTypeTag.ParsedLightTypeTag.SubtypeDBs
import izumi.reflect.macrortti.LightTypeTagRef.SymName.{LambdaParamName, SymLiteral, SymTermName, SymTypeName}
import izumi.reflect.macrortti.LightTypeTagRef._
import izumi.reflect.thirdparty.internal.boopickle.NoMacro.Pickler
import izumi.reflect.thirdparty.internal.boopickle.{PickleImpl, UnpickleState}
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
import scala.collection.immutable.HashSet
/**
* Extracts internal databases from [[LightTypeTag]].
* Should not be used under normal circumstances.
*
* Internal API: binary compatibility not guaranteed.
*/
final case class LightTypeTagUnpacker(tag: LightTypeTag) {
def bases: Map[AbstractReference, Set[AbstractReference]] = tag.basesdb
def inheritance: Map[NameReference, Set[NameReference]] = tag.idb
}
abstract class LightTypeTag private[reflect] (
bases: () => Map[AbstractReference, Set[AbstractReference]],
inheritanceDb: () => Map[NameReference, Set[NameReference]]
) extends Serializable {
def ref: LightTypeTagRef
// full subtyping db with lambdas, parameters and variance, e.g. List[+A] <: SeqOps[A, List, List[A]], λ %0 → List[+%0] <: λ %0,%1,%2 → SeqOps[+%0, +%1, +%2]
private[reflect] lazy val basesdb: Map[AbstractReference, Set[AbstractReference]] = bases()
// class inheritance db without lambdas and without parameters, e.g. List <: SeqOps, Iterable
private[reflect] lazy val idb: Map[NameReference, Set[NameReference]] = inheritanceDb()
def binaryFormatVersion: Int
def serialize(): LightTypeTag.Serialized = {
val hashCodeRef = this.hashCode()
val strRef = PickleImpl.serializeIntoString(this.ref, LightTypeTag.lttRefSerializer)
val strDBs = PickleImpl.serializeIntoString(SubtypeDBs.make(this.basesdb, this.idb), LightTypeTag.subtypeDBsSerializer)
LightTypeTag.Serialized(hashCodeRef, strRef, strDBs, LightTypeTag.currentBinaryFormatVersion)
}
@inline final def <:<(maybeParent: LightTypeTag): Boolean = {
new LightTypeTagInheritance(this, maybeParent).isChild()
}
@inline final def =:=(other: LightTypeTag): Boolean = {
this == other
}
final def decompose: Set[LightTypeTag] = {
ref match {
case intersection: LightTypeTagRef.IntersectionReference =>
val refs = intersection.decompose // make sure to unwrap nested intersections
refs.map(LightTypeTag(_, basesdb, idb))
case _ =>
Set(this)
}
}
final def decomposeUnion: Set[LightTypeTag] = {
ref match {
case union: LightTypeTagRef.UnionReference =>
val refs = union.decomposeUnion // make sure to unwrap nested unions
refs.map(LightTypeTag(_, basesdb, idb))
case _ =>
Set(this)
}
}
/**
* Parameterize this type tag with `args` if it describes an unapplied type lambda
*
* If there are less `args` given than this type takes parameters, it will remain a type
* lambda taking remaining arguments:
*
* {{{
* F[_, _, _].combine(A, B) = F[A, B, _]
* }}}
*/
def combine(args: LightTypeTag*): LightTypeTag = {
val argRefs = args.map(_.ref)
val appliedBases = basesdb ++ basesdb.iterator.collect { // do not remove the unapplied base lambdas after combination (required for inferredLambdaParents in isChild)
case (self: LightTypeTagRef.Lambda, parents) =>
self.combine(argRefs) -> parents.map {
case l: LightTypeTagRef.Lambda =>
l.combine(argRefs)
case nonLambdaParent =>
val context = self.input.zip(argRefs.collect { case a: AbstractReference => a }).toMap
new RuntimeAPI.Rewriter(context).replaceRefs(nonLambdaParent)
}
}
def mergedBasesDB = LightTypeTag.mergeIDBs(appliedBases, args.iterator.map(_.basesdb))
def mergedInheritanceDb = LightTypeTag.mergeIDBs(idb, args.iterator.map(_.idb))
LightTypeTag(ref.combine(argRefs), mergedBasesDB, mergedInheritanceDb)
}
/**
* Parameterize this type tag with `args` if it describes an unapplied type lambda
*
* The resulting type lambda will take parameters in places where `args` was None:
*
* {{{
* F[_, _, _].combine(Some(A), None, Some(C)) = F[A, _, C]
* }}}
*/
def combineNonPos(args: Option[LightTypeTag]*): LightTypeTag = {
val argRefs = args.map(_.map(_.ref))
val appliedBases = basesdb ++ basesdb.iterator.collect { // do not remove the unapplied base lambdas after combination (required for inferredLambdaParents in isChild)
case (self: LightTypeTagRef.Lambda, parents) =>
self.combineNonPos(argRefs) -> parents.map {
case l: LightTypeTagRef.Lambda =>
l.combineNonPos(argRefs)
case nonLambdaParent =>
val context = self.input.zip(argRefs.flatten.collect { case a: AbstractReference => a }).toMap
new RuntimeAPI.Rewriter(context).replaceRefs(nonLambdaParent)
}
}
def mergedBasesDB = LightTypeTag.mergeIDBs(appliedBases, args.iterator.map(_.map(_.basesdb).getOrElse(Map.empty)))
def mergedInheritanceDb = LightTypeTag.mergeIDBs(idb, args.iterator.map(_.map(_.idb).getOrElse(Map.empty)))
LightTypeTag(ref.combineNonPos(argRefs), mergedBasesDB, mergedInheritanceDb)
}
/**
* Strip all args from type tag of parameterized type and its supertypes
* Useful for very rough type-constructor / class-only comparisons.
*
* NOTE: This DOES NOT RESTORE TYPE CONSTRUCTOR/LAMBDA and is
* NOT equivalent to .typeConstructor call in scala-reflect
* - You won't be able to call [[combine]] on result type
* and partially applied types will not work correctly
*/
@NowarnCompat.nowarn("msg=deprecated")
def withoutArgs: LightTypeTag = {
LightTypeTag(ref.withoutArgs, basesdb.mapValues(_.map(_.withoutArgs)).iterator.toMap, idb)
}
/**
* Extract arguments applied to this type constructor
*/
def typeArgs: List[LightTypeTag] = {
ref.typeArgs.map(LightTypeTag(_, basesdb, idb))
}
/**
* Remove types that are supertypes of other types in the intersection from it
*
* e.g. transform `Tag[TraitSuper & TraitChild & AnyRef]` to `Tag[TraitChild]`
*/
def removeIntersectionTautologies: LightTypeTag = {
ref match {
case IntersectionReference(refs) =>
if (refs.size <= 1) {
this
} else {
refs.tail.foldLeft(LightTypeTag(refs.head, basesdb, idb)) {
case (acc, tref) =>
val t = LightTypeTag(tref, basesdb, idb)
if (t <:< acc) {
t
} else if (acc <:< t) {
acc
} else {
LightTypeTag(LightTypeTagRef.IntersectionReference(acc.ref.decompose + tref), basesdb, idb)
}
}
}
case _ =>
this
}
}
/**
* Remove types that are subtypes of other types in the union from it
*
* e.g. transform `Tag[TraitSuper | TraitChild | Nothing]` to `Tag[TraitSuper]`
*/
def removeUnionTautologies: LightTypeTag = {
ref match {
case UnionReference(refs) =>
if (refs.size <= 1) {
this
} else {
refs.tail.foldLeft(LightTypeTag(refs.head, basesdb, idb)) {
case (acc, tref) =>
val t = LightTypeTag(tref, basesdb, idb)
if (t <:< acc) {
acc
} else if (acc <:< t) {
t
} else {
LightTypeTag(LightTypeTagRef.UnionReference(acc.ref.decomposeUnion + tref), basesdb, idb)
}
}
}
case _ =>
this
}
}
/** Render to string, omitting package names */
override def toString: String = {
ref.toString
}
/**
* Fully-qualified rendering of a type, including packages and prefix types.
* Use [[toString]] for a rendering that omits package names
*/
def repr: String = {
ref.repr
}
/**
* Fully-qualified rendering of a type, including packages and prefix types.
* Traditional Scala notation for lambdas, e.g. scala.util.Either[+scala.Int,+_]
*/
def scalaStyledRepr: String = {
ref.scalaStyledRepr
}
/** Short class or type-constructor name of this type, without package or prefix names
*
* @note This will produce only a type constructor name, for full rendering of the type use [[toString]], [[repr]] or [[scalaStyledRepr]]
*/
def shortName: String = {
ref.shortName
}
/** Class or type-constructor name of this type, with package and prefix names
*
* @note This will produce only a type constructor name, for full rendering of the type use [[toString]], [[repr]] or [[scalaStyledRepr]]
*/
def longNameWithPrefix: String = {
ref.longNameWithPrefix
}
/** Internal symbol name of type-constructor of this type, with package and containing definition names
*
* @note This will produce only a type constructor name, for full rendering of the type use [[toString]], [[repr]] or [[scalaStyledRepr]]
*/
def longNameInternalSymbol: String = {
ref.longNameInternalSymbol
}
@deprecated("Renamed to scalaStyledRepr", "3.0.8")
def scalaStyledName: String = {
ref.scalaStyledName
}
@deprecated(
"Produces Scala version dependent output, with incorrect prefixes for types with value prefixes. Use `longNameWithPrefix` instead, or `longNameInternalSymbol` for old behavior",
"2.2.2"
)
/** @deprecated Produces Scala version dependent output, with incorrect prefixes for types with value prefixes. Use `longNameWithPrefix` instead, or `longNameInternalSymbol` for old behavior */
def longName: String = {
ref.longName
}
/** Print internal structures state */
def debug(name: String = ""): String = {
s"""⚙️ begin $name: ${this.repr}
|⚡️bases:${LTTRenderables.Long.renderDb(basesdb)}
|⚡️inheritance:${LTTRenderables.Long.renderDb(idb)}
|⚙️ end $name""".stripMargin
}
override def equals(other: Any): Boolean = {
other match {
case that: LightTypeTag =>
ref == that.ref
case _ => false
}
}
override def hashCode(): Int = {
hashcode
}
private[this] lazy val hashcode: Int = {
ref.hashCode() * 31
}
}
object LightTypeTag {
final val currentBinaryFormatVersion = 30
case class Serialized(hash: Int, ref: String, databases: String, version: Int)
@inline def apply(ref0: LightTypeTagRef, bases: => Map[AbstractReference, Set[AbstractReference]], db: => Map[NameReference, Set[NameReference]]): LightTypeTag = {
new UnparsedLightTypeTag(ref0, () => bases, () => db)
}
/** Create a [[LightTypeTag]] formed of `intersection` with the structural refinement taken from `structure`
*
* @param structure the non-structural part of structure is ignored, except SubtypeDBs
* @param additionalTypeMembers additional type members
*/
def refinedType(intersection: List[LightTypeTag], structure: LightTypeTag, additionalTypeMembers: Map[String, LightTypeTag]): LightTypeTag = {
val intersectionRef = LightTypeTagRef.maybeIntersection(intersection.iterator.map(_.ref))
val ref = {
val decls = structure.ref match {
case LightTypeTagRef.Refinement(_, decls) if decls.nonEmpty =>
decls
case _ =>
Set.empty[RefinementDecl]
}
if (decls.nonEmpty || additionalTypeMembers.nonEmpty) {
val newDecls = decls.filterNot(additionalTypeMembers contains _.name) ++ additionalTypeMembers.iterator.map {
case (k, v) =>
RefinementDecl.TypeMember(k, v.ref match { case r: AbstractReference => r })
}
LightTypeTagRef.Refinement(intersectionRef, newDecls)
} else {
intersectionRef
}
}
def mergedBasesDB: Map[AbstractReference, Set[AbstractReference]] =
LightTypeTag.mergeIDBs(structure.basesdb, intersection.iterator.map(_.basesdb) ++ additionalTypeMembers.iterator.map(_._2.basesdb))
def mergedInheritanceDb: Map[NameReference, Set[NameReference]] =
LightTypeTag.mergeIDBs(structure.idb, intersection.iterator.map(_.idb) ++ additionalTypeMembers.iterator.map(_._2.idb))
LightTypeTag(ref, mergedBasesDB, mergedInheritanceDb)
}
def unionType(union: List[LightTypeTag]): LightTypeTag = {
val ref = LightTypeTagRef.maybeUnion(union.iterator.map(_.ref))
def mergedBasesDB: Map[AbstractReference, Set[AbstractReference]] =
LightTypeTag.mergeIDBs(union.iterator.flatMap(_.basesdb))
def mergedInheritanceDb: Map[NameReference, Set[NameReference]] =
LightTypeTag.mergeIDBs(union.iterator.flatMap(_.idb))
LightTypeTag(ref, mergedBasesDB, mergedInheritanceDb)
}
def wildcardType(lowTag: LightTypeTag, highTag: LightTypeTag): LightTypeTag = {
val ref = LightTypeTagRef.WildcardReference(
Boundaries.Defined(
lowTag.ref match { case r: AbstractReference => r },
highTag.ref match { case r: AbstractReference => r }
)
)
def mergedBasesDB: Map[AbstractReference, Set[AbstractReference]] =
LightTypeTag.mergeIDBs(highTag.basesdb, lowTag.basesdb)
def mergedInheritanceDb: Map[NameReference, Set[NameReference]] =
LightTypeTag.mergeIDBs(highTag.idb, lowTag.idb)
LightTypeTag(ref, mergedBasesDB, mergedInheritanceDb)
}
def parse(serialized: Serialized): LightTypeTag = {
parse[LightTypeTag](serialized.hash, serialized.ref, serialized.databases, serialized.version)
}
def parse[T](hashCode: Int, refString: String, basesString: String, version: Int): LightTypeTag = {
lazy val shared = {
subtypeDBsSerializer.unpickle(UnpickleState(ByteBuffer.wrap(basesString.getBytes(StandardCharsets.ISO_8859_1))))
}
if (version >= 0 && version < currentBinaryFormatVersion) {
// because lambda parameter names in binary format are not normalized, the hashCode of the
// binary string can/will violate equals/hashCode contract when comparing binary strings
// between different binary version format or when using any hashCode from older binary
// string. So we ignore all hashCodes and fast paths from old binary strings
new ParsedLightTypeTagOldOrPre230(binaryFormatVersion = version, refString, () => shared.bases, () => shared.idb)
} else if (version == currentBinaryFormatVersion) {
new ParsedLightTypeTag230Plus(version, hashCode, refString, () => shared.bases, () => shared.idb)
} else {
throw new LinkageError(s"""Couldn't parse a `LightTypeTag` version=$version generated by a newer version of `izumi-reflect`,
|please include a newer version of the library jar `dev.zio:izumi-reflect`!
|
|Supported `version` range in this version: 0..$currentBinaryFormatVersion""".stripMargin)
}
}
@deprecated("Binary compatibility for 1.0.0-M6+", "1.0.0-M6")
private[reflect] def refinedType(intersection: List[LightTypeTag], structure: LightTypeTag): LightTypeTag = {
refinedType(intersection, structure, Map.empty)
}
private[reflect] final class UnparsedLightTypeTag private[reflect] (
override val ref: LightTypeTagRef,
bases: () => Map[AbstractReference, Set[AbstractReference]],
inheritanceDb: () => Map[NameReference, Set[NameReference]]
) extends LightTypeTag(bases, inheritanceDb) {
@noinline override def binaryFormatVersion: Int = -1
}
private[reflect] object ParsedLightTypeTag {
private[reflect] final case class SubtypeDBs private (
bases: Map[AbstractReference, Set[AbstractReference]],
idb: Map[NameReference, Set[NameReference]]
)
private[reflect] object SubtypeDBs {
@NowarnCompat.nowarn("msg=deprecated")
private[reflect] def make(bases: Map[AbstractReference, Set[AbstractReference]], idb: Map[NameReference, Set[NameReference]]): SubtypeDBs = {
new SubtypeDBs(
bases.mapValues(_.filterNot(v => LightTypeTagRef.isIgnored(v))).filterNot(_._2.isEmpty).iterator.toMap,
idb.mapValues(_.filterNot(v => LightTypeTagRef.isIgnored(v))).filterNot(_._2.isEmpty).iterator.toMap
)
}
}
}
/** `ParsedLightTypeTag` before 2.3.0 or before current version. It is forcefully deoptimized */
private[reflect] final class ParsedLightTypeTagOldOrPre230 private[reflect] (
// disable precomputed hashCode on older binary versions
// (old serialized lambdas break equals-hashCode contract
// when compared with new serialized lambdas)
// override val hashCode: Int,
override val binaryFormatVersion: Int,
private[reflect] val refString: String,
bases: () => Map[AbstractReference, Set[AbstractReference]],
db: () => Map[NameReference, Set[NameReference]]
) extends LightTypeTag(bases, db) {
override lazy val ref: LightTypeTagRef = {
deserializeRefString(refString)
}
// disable optimizations for older binary versions
// override def equals(other: Any): Boolean = {
// other match {
// case that: ParsedLightTypeTagPre230 if this.binaryFormatVersion == that.binaryFormatVersion =>
// if (this.refString == that.refString) true
// else if (optimisticEqualsEnabled) false
// else super.equals(other)
// case _ =>
// super.equals(other)
// }
// }
}
/** `ParsedLightTypeTag` since 2.3.0 or current binary format version */
private[reflect] final class ParsedLightTypeTag230Plus private[reflect] (
override val binaryFormatVersion: Int,
override val hashCode: Int,
private[reflect] val refString: String,
bases: () => Map[AbstractReference, Set[AbstractReference]],
db: () => Map[NameReference, Set[NameReference]]
) extends LightTypeTag(bases, db) {
override lazy val ref: LightTypeTagRef = {
deserializeRefString(refString)
}
override def equals(other: Any): Boolean = {
other match {
case that: ParsedLightTypeTag230Plus if this.binaryFormatVersion == that.binaryFormatVersion =>
if (this.refString == that.refString) true
else if (optimisticEqualsEnabled) false
else super.equals(other)
case _ =>
super.equals(other)
}
}
}
@noinline private[reflect] def deserializeRefString(refString: String): LightTypeTagRef = {
lttRefSerializer.unpickle(UnpickleState(ByteBuffer.wrap(refString.getBytes(StandardCharsets.ISO_8859_1))))
}
private[this] final val optimisticEqualsEnabled = {
System
.getProperty(DebugProperties.`izumi.reflect.rtti.optimized.equals`)
.asBoolean()
.getOrElse(true)
}
private[reflect] val (lttRefSerializer: Pickler[LightTypeTagRef], subtypeDBsSerializer: Pickler[SubtypeDBs]) = {
import izumi.reflect.thirdparty.internal.boopickle
import izumi.reflect.thirdparty.internal.boopickle.BasicPicklers.IntPickler
import izumi.reflect.thirdparty.internal.boopickle.NoMacro.{Pickler => _, _}
implicit lazy val variance: Pickler[Variance] = IntPickler.xmap {
case 0 => Variance.Invariant: Variance
case 1 => Variance.Contravariant: Variance
case 2 => Variance.Covariant: Variance
} {
case Variance.Invariant => 0
case Variance.Contravariant => 1
case Variance.Covariant => 2
}
implicit lazy val symName: Pickler[SymName] = new Pickler[SymName] {
override def pickle(obj: SymName)(implicit state: PickleState): Unit = {
obj match {
case SymTermName(name) =>
state.enc.writeInt(0)
state.pickle[String](name)
()
case SymTypeName(name) =>
state.enc.writeInt(1)
state.pickle[String](name)
()
case SymLiteral(name) =>
state.enc.writeInt(2)
state.pickle[String](name)
()
case LambdaParamName(index, depth, arity) =>
state.enc.writeInt(3)
state.enc.writeInt(index)
state.enc.writeInt(depth)
state.enc.writeInt(arity)
()
}
}
override def unpickle(implicit state: UnpickleState): SymName = state.dec.readInt match {
case 0 =>
SymTermName(state.unpickle[String])
case 1 =>
SymTypeName(state.unpickle[String])
case 2 =>
SymLiteral(state.unpickle[String])
case 3 =>
LambdaParamName(state.dec.readInt, state.dec.readInt, state.dec.readInt)
case o =>
throw new IllegalArgumentException(s"Unexpected data: $o")
}
}
implicit lazy val boundariesDefined: Pickler[Boundaries.Defined] = new Pickler[Boundaries.Defined] {
override def pickle(obj: Boundaries.Defined)(implicit state: PickleState): Unit = {
Tuple2Pickler[AbstractReference, AbstractReference].pickle((obj.bottom, obj.top))
}
override def unpickle(implicit state: UnpickleState): Boundaries.Defined = {
val u = Tuple2Pickler[AbstractReference, AbstractReference].unpickle
Boundaries.Defined(u._1, u._2)
}
}
implicit lazy val boundaries: Pickler[Boundaries] = new Pickler[Boundaries] {
override def pickle(obj: Boundaries)(implicit state: PickleState): Unit = obj match {
case d: Boundaries.Defined =>
optionPickler[Boundaries.Defined].pickle(Some(d))
case Boundaries.Empty =>
optionPickler[Boundaries.Defined].pickle(None)
}
override def unpickle(implicit state: UnpickleState): Boundaries = {
optionPickler[Boundaries.Defined].unpickle match {
case Some(value) =>
value
case None =>
Boundaries.Empty
}
}
}
implicit def nameRefSerializer: Pickler[NameReference] = new boopickle.Pickler[LightTypeTagRef.NameReference] {
override def pickle(value: LightTypeTagRef.NameReference)(implicit state: boopickle.PickleState): Unit = {
{
val ref = state.identityRefFor(value)
if (ref.isDefined) state.enc.writeInt(-ref.get)
else {
state.enc.writeInt(0)
state.pickle[LightTypeTagRef.SymName](value.ref)
state.pickle[LightTypeTagRef.Boundaries](value.boundaries)
state.pickle[Option[LightTypeTagRef.AppliedReference]](value.prefix)
state.addIdentityRef(value)
}
}
()
}
override def unpickle(implicit state: boopickle.UnpickleState): LightTypeTagRef.NameReference = {
val ic = state.dec.readInt
if (ic == 0) {
val value = new LightTypeTagRef.NameReference(
state.unpickle[LightTypeTagRef.SymName],
state.unpickle[LightTypeTagRef.Boundaries],
state.unpickle[Option[LightTypeTagRef.AppliedReference]]
)
state.addIdentityRef(value)
value
} else if (ic < 0)
state.identityFor[LightTypeTagRef.NameReference](-ic)
else
state.codingError(ic)
}
}
implicit lazy val appliedref: Pickler[AppliedReference] = new boopickle.CompositePickler[LightTypeTagRef.AppliedReference] {
addConcreteType[FullReference]
addConcreteType[IntersectionReference]
addConcreteType[NameReference]
addConcreteType[Refinement]
addConcreteType[UnionReference]
addConcreteType[WildcardReference]
}
implicit lazy val aref: Pickler[AbstractReference] = new boopickle.CompositePickler[LightTypeTagRef.AbstractReference] {
addConcreteType[FullReference]
addConcreteType[IntersectionReference]
addConcreteType[LightTypeTagRef.Lambda]
addConcreteType[NameReference]
addConcreteType[Refinement]
addConcreteType[UnionReference]
addConcreteType[WildcardReference]
}
implicit lazy val tagref: Pickler[LightTypeTagRef] = new boopickle.CompositePickler[LightTypeTagRef] {
addConcreteType[FullReference]
addConcreteType[IntersectionReference]
addConcreteType[LightTypeTagRef.Lambda]
addConcreteType[NameReference]
addConcreteType[Refinement]
addConcreteType[UnionReference]
addConcreteType[WildcardReference]
}
implicit lazy val wildcardRefSerializer: Pickler[WildcardReference] = new boopickle.Pickler[LightTypeTagRef.WildcardReference] {
override def pickle(value: LightTypeTagRef.WildcardReference)(implicit state: boopickle.PickleState): Unit = {
{
val ref = state.identityRefFor(value)
if (ref.isDefined) state.enc.writeInt(-ref.get)
else {
state.enc.writeInt(0)
state.pickle[LightTypeTagRef.Boundaries](value.boundaries)
}
}
()
}
override def unpickle(implicit state: boopickle.UnpickleState): LightTypeTagRef.WildcardReference = {
val ic = state.dec.readInt
if (ic == 0) {
val value = new WildcardReference(
state.unpickle[Boundaries]
)
state.addIdentityRef(value)
value
} else if (ic < 0)
state.identityFor[LightTypeTagRef.WildcardReference](-ic)
else
state.codingError(ic)
}
}
// Deserializer for FullReference before version 2.3.0 (with String first parameter instead of SymName)
implicit lazy val fullRef: Pickler[FullReference] = new boopickle.Pickler[LightTypeTagRef.FullReference] {
override def pickle(value: LightTypeTagRef.FullReference)(implicit state: boopickle.PickleState): Unit = {
{
val ref = state.identityRefFor(value)
if (ref.isDefined) state.enc.writeInt(-ref.get)
else {
// After version 2.3.0 the FullReference format changed - we now use SymName not String for FullReference name
// Thankfully, boopickle's identity serialization only uses number range Int.MinValue..0,
// leaving us with all numbers above 0 to encode format variants.
state.enc.writeInt(1) // We use 1 instead of normal value 0
//
state.pickle[SymName](value.symName)
state.pickle[List[LightTypeTagRef.TypeParam]](value.parameters)
state.pickle[Option[LightTypeTagRef.AppliedReference]](value.prefix)
state.addIdentityRef(value)
}
}
()
}
override def unpickle(implicit state: boopickle.UnpickleState): LightTypeTagRef.FullReference = {
val ic = state.dec.readInt
if (ic == 0) { // Pre 2.3.0 format
val value = LightTypeTagRef.FullReference(
SymName.SymTypeName(state.unpickle[String]),
state.unpickle[List[LightTypeTagRef.TypeParam]],
state.unpickle[Option[LightTypeTagRef.AppliedReference]]
)
state.addIdentityRef(value)
value
} else if (ic == 1) { // Post 2.3.0
val value = LightTypeTagRef.FullReference(
state.unpickle[SymName],
state.unpickle[List[LightTypeTagRef.TypeParam]],
state.unpickle[Option[LightTypeTagRef.AppliedReference]]
)
state.addIdentityRef(value)
value
} else if (ic < 0)
state.identityFor[LightTypeTagRef.FullReference](-ic)
else
state.codingError(ic)
}
}
implicit lazy val typeParam: Pickler[LightTypeTagRef.TypeParam] = new boopickle.Pickler[LightTypeTagRef.TypeParam] {
override def pickle(value: LightTypeTagRef.TypeParam)(implicit state: boopickle.PickleState): Unit = {
{
val ref = state.identityRefFor(value)
if (ref.isDefined) state.enc.writeInt(-ref.get)
else {
state.enc.writeInt(0)
state.pickle[LightTypeTagRef.AbstractReference](value.ref)
state.pickle[LightTypeTagRef.Variance](value.variance)
state.addIdentityRef(value)
}
}
()
}
override def unpickle(implicit state: boopickle.UnpickleState): LightTypeTagRef.TypeParam = {
val ic = state.dec.readInt
if (ic == 0) {
val value = LightTypeTagRef.TypeParam(state.unpickle[LightTypeTagRef.AbstractReference], state.unpickle[LightTypeTagRef.Variance])
state.addIdentityRef(value)
value
} else if (ic < 0)
state.identityFor[LightTypeTagRef.TypeParam](-ic)
else
state.codingError(ic)
}
}
implicit lazy val union: Pickler[UnionReference] = new boopickle.Pickler[LightTypeTagRef.UnionReference] {
override def pickle(value: LightTypeTagRef.UnionReference)(implicit state: boopickle.PickleState): Unit = {
{
val ref = state.identityRefFor(value)
if (ref.isDefined) state.enc.writeInt(-ref.get)
else {
state.enc.writeInt(0)
state.pickle[ArraySeqLike[LightTypeTagRef.AppliedReference]](LightTypeTagRef.refSetToSortedArray[AppliedReference](value.refs))
state.addIdentityRef(value)
}
}
()
}
override def unpickle(implicit state: boopickle.UnpickleState): LightTypeTagRef.UnionReference = {
val ic = state.dec.readInt
if (ic == 0) {
val refs0 = state.unpickle[HashSet[AppliedReference]]
val refs = refs0.flatMap {
case u: UnionReference => u.decomposeUnion
case other: AppliedReferenceExceptUnion => Set(other)
}
val value = LightTypeTagRef.UnionReference(refs)
state.addIdentityRef(value)
value
} else if (ic < 0)
state.identityFor[LightTypeTagRef.UnionReference](-ic)
else
state.codingError(ic)
}
}
implicit lazy val intersection: Pickler[IntersectionReference] = new boopickle.Pickler[LightTypeTagRef.IntersectionReference] {
override def pickle(value: LightTypeTagRef.IntersectionReference)(implicit state: boopickle.PickleState): Unit = {
{
val ref = state.identityRefFor(value)
if (ref.isDefined) state.enc.writeInt(-ref.get)
else {
state.enc.writeInt(0)
state.pickle[ArraySeqLike[LightTypeTagRef.AppliedReference]](LightTypeTagRef.refSetToSortedArray[AppliedReference](value.refs))
state.addIdentityRef(value)
}
}
()
}
override def unpickle(implicit state: boopickle.UnpickleState): LightTypeTagRef.IntersectionReference = {
val ic = state.dec.readInt
if (ic == 0) {
val refs0 = state.unpickle[HashSet[AppliedReference]]
val refs = refs0.flatMap {
case u: IntersectionReference => u.decompose
case other: AppliedReferenceExceptIntersection => Set(other)
}
val value = LightTypeTagRef.IntersectionReference(refs)
state.addIdentityRef(value)
value
} else if (ic < 0)
state.identityFor[LightTypeTagRef.IntersectionReference](-ic)
else
state.codingError(ic)
}
}
implicit lazy val lambda: Pickler[LightTypeTagRef.Lambda] = new boopickle.Pickler[LightTypeTagRef.Lambda] {
override def pickle(value: LightTypeTagRef.Lambda)(implicit state: boopickle.PickleState): Unit = {
{
val ref = state.identityRefFor(value)
if (ref.isDefined) state.enc.writeInt(-ref.get)
else {
// After version 2.3.0 the Lambda format changed - we now use LambdaParamName not LambdaParameter for Lambda inputs
// Thankfully, boopickle's identity serialization only uses number range Int.MinValue..0,
// leaving us with all numbers above 0 to encode format variants.
state.enc.writeInt(1) // We use 1 instead of normal value 0
state.pickle[List[SymName.LambdaParamName]](value.input)
state.pickle[LightTypeTagRef.AbstractReference](value.output)
state.addIdentityRef(value)
}
}
()
}
override def unpickle(implicit state: boopickle.UnpickleState): LightTypeTagRef.Lambda = {
val ic = state.dec.readInt
if (ic == 0) { // Pre 2.3.0 format
object OldLambdaParameter {
type OldLambdaParameter <: String
implicit val oldLambdaParameterPickler: boopickle.Pickler[OldLambdaParameter] = new boopickle.Pickler[OldLambdaParameter] {
override def pickle(obj: OldLambdaParameter.OldLambdaParameter)(implicit state: PickleState): Unit = {
throw new RuntimeException("Impossible - old lambda parameter compat pickler should never be called to serialize - only to deserialize")
}
override def unpickle(implicit state: UnpickleState): OldLambdaParameter.OldLambdaParameter = {
val ic = state.dec.readInt
if (ic == 0) {
val value = state.unpickle[String]
state.addIdentityRef(value)
value.asInstanceOf[OldLambdaParameter]
} else if (ic < 0)
state.identityFor[String](-ic).asInstanceOf[OldLambdaParameter]
else
state.codingError(ic)
}
}
}
def convertPre230LambdaToNew(lambda: LightTypeTagRef.Lambda, paramMap: Map[String, SymName.LambdaParamName]): LightTypeTagRef.Lambda = {
def goReplaceBoundaries(boundaries: Boundaries): Boundaries = boundaries match {
case Boundaries.Defined(bottom, top) =>
Boundaries.Defined(goReplace(bottom), goReplace(top))
case Boundaries.Empty =>
Boundaries.Empty
}
def goReplaceSymName(symName: SymName): SymName = symName match {
case oldSymName @ SymTypeName(maybeLambdaParam) =>
paramMap.getOrElse(maybeLambdaParam, oldSymName)
case _ => symName
}
def goReplace[T <: AbstractReference](abstractReference: T): T = {
(abstractReference match {
case Lambda(input, output) =>
// a lambda read by another codec must have already
// been processed by conversion procedure, so it should
// have no clashes in SymTypeName with old parameters
// anymore and be safe to process
Lambda(input, goReplace(output))
case IntersectionReference(refs) =>
IntersectionReference(refs.map(goReplace))
case UnionReference(refs) =>
UnionReference(refs.map(goReplace))
case WildcardReference(boundaries) =>
WildcardReference(goReplaceBoundaries(boundaries))
case Refinement(reference, decls) =>
Refinement(
goReplace(reference),
decls.map {
case RefinementDecl.Signature(name, input, output) =>
RefinementDecl.Signature(name, input.map(goReplace), goReplace(output))
case RefinementDecl.TypeMember(name, ref) =>
RefinementDecl.TypeMember(name, goReplace(ref))
}
)
case NameReference(ref, boundaries, prefix) =>
NameReference(goReplaceSymName(ref), goReplaceBoundaries(boundaries), prefix.map(goReplace))
case FullReference(symName, parameters, prefix) =>
FullReference(
goReplaceSymName(symName),
parameters.map {
case TypeParam(ref, variance) => TypeParam(goReplace(ref), variance)
},
prefix.map(goReplace)
)
}).asInstanceOf[T]
}
val LightTypeTagRef.Lambda(convertedParams, oldResult) = lambda
LightTypeTagRef.Lambda(convertedParams, goReplace(oldResult))
}
import OldLambdaParameter.{OldLambdaParameter, oldLambdaParameterPickler}
val oldParams = state.unpickle[List[OldLambdaParameter]]
val lambdaResult = state.unpickle[LightTypeTagRef.AbstractReference]
val convertedParams = oldParams.map(SymName.bincompatForceCreateLambdaParamNameFromString(_))
val paramMap = oldParams.iterator.zip(convertedParams.iterator).toMap[String, SymName.LambdaParamName]
val oldLambda = LightTypeTagRef.Lambda(convertedParams, lambdaResult)
val value = convertPre230LambdaToNew(oldLambda, paramMap)
state.addIdentityRef(value)
value
} else if (ic == 1) { // Post 2.3.0
val value = LightTypeTagRef.Lambda(
state.unpickle[List[SymName.LambdaParamName]],
state.unpickle[LightTypeTagRef.AbstractReference]
)
state.addIdentityRef(value)
value
} else if (ic < 0)
state.identityFor[LightTypeTagRef.Lambda](-ic)
else
state.codingError(ic)
}
}
implicit lazy val lambdaParameter: Pickler[SymName.LambdaParamName] = new boopickle.Pickler[SymName.LambdaParamName] {
override def pickle(value: SymName.LambdaParamName)(implicit state: boopickle.PickleState): Unit = {
{
val ref = state.identityRefFor(value)
if (ref.isDefined) state.enc.writeInt(-ref.get)
else {
state.enc.writeInt(0)
state.pickle[Int](value.index)
state.pickle[Int](value.depth)
state.pickle[Int](value.arity)
state.addIdentityRef(value)
}
}
()
}
override def unpickle(implicit state: boopickle.UnpickleState): SymName.LambdaParamName = {
val ic = state.dec.readInt
if (ic == 0) {
val value = SymName.LambdaParamName(state.unpickle[Int], state.unpickle[Int], state.unpickle[Int])
state.addIdentityRef(value)
value
} else if (ic < 0)
state.identityFor[SymName.LambdaParamName](-ic)
else
state.codingError(ic)
}
}
implicit lazy val refinement: Pickler[Refinement] = new boopickle.Pickler[LightTypeTagRef.Refinement] {
override def pickle(value: LightTypeTagRef.Refinement)(implicit state: boopickle.PickleState): Unit = {
{
val ref = state.identityRefFor(value)
if (ref.isDefined) state.enc.writeInt(-ref.get)
else {
state.enc.writeInt(0)
state.pickle[LightTypeTagRef.AppliedReference](value.reference)
state.pickle[ArraySeqLike[LightTypeTagRef.RefinementDecl]](LightTypeTagRef.refinementDeclSetToSortedArray(value.decls))
state.addIdentityRef(value)
}
}
()
}
override def unpickle(implicit state: boopickle.UnpickleState): LightTypeTagRef.Refinement = {
val ic = state.dec.readInt
if (ic == 0) {
val value = LightTypeTagRef.Refinement(state.unpickle[LightTypeTagRef.AppliedReference], state.unpickle[HashSet[LightTypeTagRef.RefinementDecl]])
state.addIdentityRef(value)
value
} else if (ic < 0)
state.identityFor[LightTypeTagRef.Refinement](-ic)
else
state.codingError(ic)
}
}
implicit lazy val refinementDecl: boopickle.CompositePickler[LightTypeTagRef.RefinementDecl] = new boopickle.CompositePickler[LightTypeTagRef.RefinementDecl] {
addConcreteType[LightTypeTagRef.RefinementDecl.Signature]
addConcreteType[LightTypeTagRef.RefinementDecl.TypeMember]
}
implicit lazy val typeMember: Pickler[RefinementDecl.TypeMember] = new boopickle.Pickler[LightTypeTagRef.RefinementDecl.TypeMember] {
override def pickle(value: LightTypeTagRef.RefinementDecl.TypeMember)(implicit state: boopickle.PickleState): Unit = {
{
val ref = state.identityRefFor(value)
if (ref.isDefined) state.enc.writeInt(-ref.get)
else {
state.enc.writeInt(0)
state.pickle[String](value.name)
state.pickle[LightTypeTagRef.AbstractReference](value.ref)
state.addIdentityRef(value)
}
}
()
}
override def unpickle(implicit state: boopickle.UnpickleState): LightTypeTagRef.RefinementDecl.TypeMember = {
val ic = state.dec.readInt
if (ic == 0) {
val value = LightTypeTagRef.RefinementDecl.TypeMember(state.unpickle[String], state.unpickle[LightTypeTagRef.AbstractReference])
state.addIdentityRef(value)
value
} else if (ic < 0)
state.identityFor[LightTypeTagRef.RefinementDecl.TypeMember](-ic)
else
state.codingError(ic)
}
}
implicit lazy val signature: Pickler[RefinementDecl.Signature] = new boopickle.Pickler[LightTypeTagRef.RefinementDecl.Signature] {
override def pickle(value: LightTypeTagRef.RefinementDecl.Signature)(implicit state: boopickle.PickleState): Unit = {
{
val ref = state.identityRefFor(value)
if (ref.isDefined) state.enc.writeInt(-ref.get)
else {
state.enc.writeInt(0)
state.pickle[String](value.name)
state.pickle[List[LightTypeTagRef.AppliedReference]](value.input)
state.pickle[LightTypeTagRef.AppliedReference](value.output)
state.addIdentityRef(value)
}
}
()
}
override def unpickle(implicit state: boopickle.UnpickleState): LightTypeTagRef.RefinementDecl.Signature = {
val ic = state.dec.readInt
if (ic == 0) {
val value = LightTypeTagRef
.RefinementDecl.Signature(
state.unpickle[String],
state.unpickle[List[LightTypeTagRef.AppliedReference]],
state.unpickle[LightTypeTagRef.AppliedReference]
)
state.addIdentityRef(value)
value
} else if (ic < 0)
state.identityFor[LightTypeTagRef.RefinementDecl.Signature](-ic)
else
state.codingError(ic)
}
}
implicit lazy val dbsSerializer: Pickler[SubtypeDBs] = new boopickle.Pickler[LightTypeTag.ParsedLightTypeTag.SubtypeDBs] {
override def pickle(value: LightTypeTag.ParsedLightTypeTag.SubtypeDBs)(implicit state: boopickle.PickleState): Unit = {
val ref = state.identityRefFor(value)
if (ref.isDefined) state.enc.writeInt(-ref.get)
else {
state.enc.writeInt(0)
state.pickle[Map[LightTypeTagRef.AbstractReference, Set[LightTypeTagRef.AbstractReference]]](value.bases)
state.pickle[Map[LightTypeTagRef.NameReference, Set[LightTypeTagRef.NameReference]]](value.idb)
state.addIdentityRef(value)
}
()
}
override def unpickle(implicit state: boopickle.UnpickleState): LightTypeTag.ParsedLightTypeTag.SubtypeDBs = {
val ic = state.dec.readInt
if (ic == 0) {
val value = LightTypeTag
.ParsedLightTypeTag.SubtypeDBs.make(
state.unpickle[Map[LightTypeTagRef.AbstractReference, Set[LightTypeTagRef.AbstractReference]]],
state.unpickle[Map[LightTypeTagRef.NameReference, Set[LightTypeTagRef.NameReference]]]
)
state.addIdentityRef(value)
value
} else if (ic < 0)
state.identityFor[LightTypeTag.ParsedLightTypeTag.SubtypeDBs](-ic)
else
state.codingError(ic)
}
}
// false positive unused warnings
// lazy val _ = (symTypeName, symTermName, symName, appliedRefSerializer, nameRefSerializer, abstractRefSerializer)
(tagref, dbsSerializer)
}
private[macrortti] def mergeIDBs[T](self: Map[T, Set[T]], other: Map[T, Set[T]]): Map[T, Set[T]] = {
mergeIDBs(self.iterator ++ other.iterator)
}
private[macrortti] def mergeIDBs[T](self: Map[T, Set[T]], others: Iterator[Map[T, Set[T]]]): Map[T, Set[T]] = {
mergeIDBs(self.iterator ++ others.flatMap(_.iterator))
}
private[macrortti] def mergeIDBs[T](bases: Iterator[(T, Set[T])]): Map[T, Set[T]] = {
import izumi.reflect.internal.fundamentals.collections.IzCollections._
bases.toMultimap.map {
case (k, v) =>
(k, v.flatten.filterNot(_ == k))
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala/izumi/reflect/macrortti/LightTypeTagInheritance.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.macrortti
import izumi.reflect.internal.fundamentals.collections.ImmutableMultiMap
import izumi.reflect.internal.fundamentals.platform.basics.IzBoolean._
import izumi.reflect.internal.fundamentals.platform.console.TrivialLogger
import izumi.reflect.internal.fundamentals.platform.console.TrivialLogger.Config
import izumi.reflect.macrortti.LightTypeTagInheritance._
import izumi.reflect.macrortti.LightTypeTagRef.SymName.SymTypeName
import izumi.reflect.macrortti.LightTypeTagRef._
import scala.collection.mutable
object LightTypeTagInheritance {
private[reflect] final val tpeNothing = NameReference(SymTypeName("scala.Nothing"))
private[reflect] final val tpeNull = NameReference(SymTypeName("scala.Null"))
private[reflect] final val tpeAny = NameReference(SymTypeName("scala.Any"))
private[reflect] final val tpeAnyRef = NameReference(SymTypeName("scala.AnyRef"))
private[reflect] final val tpeMatchable = NameReference(SymTypeName("scala.Matchable"))
private[reflect] final val tpeObject = NameReference(SymTypeName(classOf[Object].getName))
private final case class Ctx(
logger: TrivialLogger,
self: LightTypeTagInheritance
) {
def next(): Ctx = Ctx(logger.sub(), self)
}
private implicit final class CtxExt(private val ctx: Ctx) extends AnyVal {
def isChild(selfT0: LightTypeTagRef, thatT0: LightTypeTagRef): Boolean = ctx.self.isChild(ctx.next())(selfT0, thatT0)
}
}
final class LightTypeTagInheritance(self: LightTypeTag, other: LightTypeTag) {
private[this] lazy val basesdb: ImmutableMultiMap[AbstractReference, AbstractReference] = LightTypeTag.mergeIDBs(self.basesdb, other.basesdb)
private[this] lazy val idb: ImmutableMultiMap[NameReference, NameReference] = LightTypeTag.mergeIDBs(self.idb, other.idb)
def isChild(): Boolean = {
val st = self.ref
val ot = other.ref
val logger = TrivialLogger.make[this.type](config = Config.console)
logger.log(s"""⚙️ Inheritance check: ${self.repr} < ${other.repr}
|⚡️bases: ${LTTRenderables.Long.renderDb(basesdb)}
|⚡️inheritance: ${LTTRenderables.Long.renderDb(idb)}""".stripMargin)
isChild(Ctx(logger, this))(st, ot)
}
private def isChild(ctx: Ctx)(selfT: LightTypeTagRef, thatT: LightTypeTagRef): Boolean = {
import ctx._
logger.log(s"✴️ isChild: `${selfT.repr}` <:< `${thatT.repr}`, context = $ctx")
val result = (selfT, thatT) match {
case (s, t) if s == t =>
true
case (s, _) if s == tpeNothing =>
true
case (s, _) if s == tpeNull =>
// TODO: we may want to check that in case of anyref target type is not a primitve (though why?)
true
case (_, t) if t == tpeAny || t == tpeAnyRef || t == tpeObject || t == tpeMatchable =>
// TODO: we may want to check that in case of anyref target type is not a primitve (though why?)
true
case (s: WildcardReference, t: WildcardReference) =>
s.boundaries match {
case Boundaries.Defined(_, top) =>
compareBounds(ctx)(top, t.boundaries)
case Boundaries.Empty =>
t.boundaries match {
case Boundaries.Defined(_, _) =>
false
case Boundaries.Empty =>
true
}
}
case (s: AppliedNamedReference, t: WildcardReference) =>
compareBounds(ctx)(s, t.boundaries)
case (s: Lambda, t: WildcardReference) =>
isChild(ctx.next())(s.output, t)
case (s: WildcardReference, t) =>
s.boundaries match {
case Boundaries.Defined(_, top) =>
ctx.next().isChild(top, t)
case Boundaries.Empty =>
s == t
}
// parameterized type
case (s: FullReference, t: FullReference) =>
compareParameterizedRefs(ctx)(s, t)
case (s: FullReference, t: NameReference) =>
any(
oneOfParameterizedParentsIsInheritedFrom(ctx)(s, t),
all(
compareBounds(ctx)(s, t.boundaries),
any(
oneOfUnparameterizedParentsIsInheritedFrom(ctx)(s.asName, t), // constructor inherits from rhs, where rhs is an unparameterized type
// outerLambdaParams.map(_.name).contains(t.ref.name), // lambda parameter may accept anything within bounds // UNSOUND-LAMBDA-COMPARISON
// t.ref.maybeName.exists(outerDecls.map(_.name).contains), // refinement type decl may accept anything within bounds,
isInBoundsOfAnEqualBoundedAbstractType(s, t) // equal-bounded abstract type
)
)
)
case (s: NameReference, t: FullReference) =>
oneOfParameterizedParentsIsInheritedFrom(ctx)(s, t)
// unparameterized type
case (s: NameReference, t: NameReference) =>
val boundIsOk = compareBounds(ctx)(s, t.boundaries)
any(
boundIsOk && any(
oneOfParameterizedParentsIsInheritedFrom(ctx)(s, t),
oneOfUnparameterizedParentsIsInheritedFrom(ctx)(s, t),
// outerLambdaParams.map(_.name).contains(t.ref.name), // lambda parameter may accept anything within bounds // UNSOUND-LAMBDA-COMPARISON
// t.ref.maybeName.exists(outerDecls.map(_.name).contains), // refinement decl may accept anything within bounds
isInBoundsOfAnEqualBoundedAbstractType(s, t) // equal-bounded abstract type
),
s.boundaries match {
case Boundaries.Defined(_, sUp) =>
ctx.isChild(sUp, t)
case Boundaries.Empty =>
false
}
)
// lambdas
case (s: AppliedNamedReference, t: Lambda) =>
isChild(ctx.next())(s, t.output)
case (s: Lambda, t: AppliedNamedReference) =>
isChild(ctx.next())(s.output, t)
case (s: Lambda, o: Lambda) =>
(s.input.size == o.input.size
&& isChild(ctx.next())(s.normalizedOutput, o.normalizedOutput))
// intersections
case (s: IntersectionReference, t: IntersectionReference) =>
// yeah, this shit is quadratic
t.refs.forall {
p =>
s.refs.exists {
c =>
ctx.isChild(c, p)
}
}
case (s: IntersectionReference, t: LightTypeTagRef) =>
s.refs.exists(c => ctx.isChild(c, t))
case (s: LightTypeTagRef, o: IntersectionReference) =>
o.refs.forall(t => ctx.isChild(s, t))
// unions
case (s: UnionReference, t: UnionReference) =>
// yeah, this shit is quadratic
s.refs.forall {
c =>
t.refs.exists {
p =>
ctx.isChild(c, p)
}
}
case (s: UnionReference, t: LightTypeTagRef) =>
s.refs.forall(c => ctx.isChild(c, t))
case (s: LightTypeTagRef, o: UnionReference) =>
o.refs.exists(t => ctx.isChild(s, t))
// refinements
case (s: Refinement, t: Refinement) =>
ctx.isChild(s.reference, t.reference) && compareDecls(ctx.next())(s.decls, t.decls)
case (s: Refinement, t: LightTypeTagRef) =>
ctx.isChild(s.reference, t)
case (s: AbstractReference, t: Refinement) =>
oneOfParameterizedParentsIsInheritedFrom(ctx)(s, t)
}
logger.log(s"${if (result) "✅" else "⛔️"} isChild: `${selfT.repr}` <:< `${thatT.repr}` == $result")
result
}
private def compareBounds(ctx: Ctx)(s: AbstractReference, t: Boundaries): Boolean = {
t match {
case Boundaries.Defined(tLow, tUp) =>
ctx.isChild(s, tUp) && ctx.isChild(tLow, s)
case Boundaries.Empty =>
true
}
}
private def compareDecls(ctx: Ctx)(sDecls: Set[RefinementDecl], tDecls: Set[RefinementDecl]): Boolean = {
val s = sDecls.groupBy(_.name)
// for every decl on the right there's a <: decl on the left
tDecls.forall {
r =>
val lOverrides = s.get(r.name).toSet.flatten
lOverrides.exists(compareDecl(ctx)(_, r))
}
}
private def compareDecl(ctx: Ctx)(s: RefinementDecl, t: RefinementDecl): Boolean = (s, t) match {
case (
RefinementDecl.TypeMember(ln, lref),
RefinementDecl.TypeMember(rn, NameReference(SymName.SymTypeName(rn1), rBounds, None))
) if rn == rn1 =>
// we're comparing two abstract types type X = X|>:A<:B|
// We know that the type is abstract if its name matches the type member's name
ln == rn && compareBounds(ctx)(lref, rBounds)
case (RefinementDecl.TypeMember(ln, lref), RefinementDecl.TypeMember(rn, rref)) =>
// if the rhs type is not abstract (has form `type X = Int`), then lhs must be exactly equal to it, not <:
ln == rn && lref == rref
case (RefinementDecl.Signature(ln, lins, lout), RefinementDecl.Signature(rn, rins, rout)) =>
(ln == rn
&& lins.iterator.zipAll(rins.iterator, null, null).forall {
case (null, _) => false
case (_, null) => false
case (l, r) => ctx.isChild(r, l) // contravariant
}
&& ctx.isChild(lout, rout)) // covariant
case _ =>
false
}
private def compareParameterizedRefs(ctx: Ctx)(self: FullReference, that: FullReference): Boolean = {
def parametersConform: Boolean = {
self.parameters.zipAll(that.parameters, null, null).forall {
case (ps, pt) =>
(ps ne null) && (pt ne null) && (pt.variance match {
case Variance.Invariant =>
pt.ref match {
case wc: LightTypeTagRef.WildcardReference =>
compareBounds(ctx)(ps.ref, wc.boundaries)
case _ =>
ps.ref == pt.ref
}
case Variance.Contravariant =>
pt.ref match {
case wc: LightTypeTagRef.WildcardReference =>
wc.boundaries match {
case Boundaries.Defined(bottom, _) =>
ctx.isChild(bottom, ps.ref)
case Boundaries.Empty =>
true
}
case _ =>
ctx.isChild(pt.ref, ps.ref)
}
case Variance.Covariant =>
pt.ref match {
case wc: LightTypeTagRef.WildcardReference =>
wc.boundaries match {
case Boundaries.Defined(_, top) =>
ctx.isChild(ps.ref, top)
case Boundaries.Empty =>
true
}
case _ =>
ctx.isChild(ps.ref, pt.ref)
}
})
}
}
val selfNameRef = self.asName
val thatNameRef = that.asName
ctx
.logger.log(
s"⚠️ comparing parameterized references: `${self.repr}` <:< `${that.repr}`, paramsOk = $parametersConform, ctorsOk = ${selfNameRef == thatNameRef}, sameArity = ${self.parameters.size == that.parameters.size}, context = $ctx"
)
if (selfNameRef == thatNameRef) {
parametersConform
} else if (oneOfParameterizedParentsIsInheritedFrom(ctx)(self, that)) {
true
} else {
val selfNormalizedLambdaParamsSize = {
// 0 unless we're in a lambda vs. lambda comparison (s.normalizedOutput <:< t.normalizedOutput)
self.parameters.count(p => isFakeParam(p.ref))
}
val appliedLambdaParents = basesdb
.iterator.collect {
case (l: Lambda, lparents) if isSameNamedRef(l.output, selfNameRef) =>
lparents.flatMap {
case lp: Lambda =>
val scala2FullDbFormLambda = {
if (lp.input.size == self.parameters.size) {
List(lp.combine(self.parameters.map(_.ref)))
} else {
Nil
}
}
val scala3FullDbFormLambda = if (lp.input.size == selfNormalizedLambdaParamsSize) {
val ps = self.parameters.collect { case FakeParam(pRef) => pRef }
val selfPs = ps.sortBy(_.ref.asInstanceOf[SymName.LambdaParamName].index)
val res = lp.combine(selfPs)
List(res)
} else Nil
scala2FullDbFormLambda ++ scala3FullDbFormLambda
case _ =>
Nil
}
}.flatten.toList
ctx.logger.log {
s"ℹ️ checking applied lambda parents of self=`${self.repr}`: parents=${appliedLambdaParents.map(_.repr)} <:< that=`${that.repr}`"
}
appliedLambdaParents.exists(ctx.isChild(_, that))
}
}
private def isSameNamedRef(a: AbstractReference, b: AbstractReference): Boolean = {
(a, b) match {
case (an: AppliedNamedReference, ab: AppliedNamedReference) =>
an.asName == ab.asName
case _ =>
false
}
}
def isFakeParam(reference: LightTypeTagRef.AbstractReference): Boolean = reference match {
case reference: NameReference =>
reference.symName match {
case l: SymName.LambdaParamName if l.depth == LightTypeTagRef.LambdaConstants.lambdaFakeParamDepth => true
case _ => false
}
case _ => false
}
object FakeParam {
def unapply(tparam: TypeParam): Option[LightTypeTagRef.NameReference] = {
if (isFakeParam(tparam.ref)) Some(tparam.ref.asInstanceOf[NameReference]) else None
}
}
private def parameterizedParentsOf(t: AbstractReference): Set[AbstractReference] = {
val basesDbParents = basesdb.getOrElse(t, Set.empty)
val withBoundaryParents = t match {
case NameReference(_, b: Boundaries.Defined, _) =>
basesDbParents + b.top
case WildcardReference(b: Boundaries.Defined) =>
basesDbParents + b.top
case _ =>
basesDbParents
}
withBoundaryParents
}
private def oneOfParameterizedParentsIsInheritedFrom(ctx: Ctx)(child: AbstractReference, parent: AbstractReference): Boolean = {
// ctx.logger.log(s"Checking if ${parameterizedParentsOf(child)} has a parent of $parent")
val parents = parameterizedParentsOf(child)
ctx.logger.log(s"Looking up parameterized parents of $child => $parents")
parents.exists(ctx.isChild(_, parent))
}
private def oneOfUnparameterizedParentsIsInheritedFrom(ctx: Ctx)(child: NameReference, parent: NameReference): Boolean = {
val parents = unparameterizedParentsOf(child)
ctx.logger.log(s"Looking up unparameterized parents of $child => $parents")
parents.exists(ctx.isChild(_, parent))
}
private def isInBoundsOfAnEqualBoundedAbstractType(child: AbstractReference, parent: NameReference): Boolean = {
parent.boundaries match {
case Boundaries.Defined(bottom, top) if top == bottom && top == child => true
case _ => false
}
}
private def unparameterizedParentsOf(t: NameReference): mutable.HashSet[NameReference] = {
def parentsOf(t: NameReference, out: mutable.HashSet[NameReference], tested: mutable.HashSet[NameReference]): Unit = {
val direct = idb.get(t).toSet.flatten
tested += t
out ++= direct
val nextNames = direct.map(_.asName)
nextNames
.diff(tested)
.foreach {
b =>
parentsOf(b.asName, out, tested)
}
}
val out = mutable.HashSet[NameReference]()
val tested = mutable.HashSet[NameReference]()
parentsOf(t, out, tested)
out
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala/izumi/reflect/macrortti/LightTypeTagRef.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.macrortti
import izumi.reflect.macrortti.LightTypeTagRef.{AbstractReference, AppliedReference}
import izumi.reflect.macrortti.LightTypeTagRef.SymName.{LambdaParamName, SymTypeName}
import scala.runtime.AbstractFunction3
import scala.util.hashing.MurmurHash3
import scala.util.{Failure, Success, Try}
sealed trait LightTypeTagRef extends LTTSyntax with Serializable {
// bincompat with versions before 2.3.0
final def combine(args: Seq[LightTypeTagRef]): AbstractReference = this.combineImpl(args)
final def combineNonPos(args: Seq[Option[LightTypeTagRef]]): AbstractReference = this.combineNonPosImpl(args)
final def withoutArgs: AbstractReference = this.withoutArgsImpl
/** Render to string, omitting package names */
override final def toString: String = this.toStringImpl
/** Fully-qualified rendering of a type, including packages and prefix types.
* Use [[toString]] for a rendering that omits package names
*/
final def repr: String = this.reprImpl
final def scalaStyledRepr: String = this.scalaStyledReprImpl
@deprecated("Renamed to scalaStyledRepr", "3.0.8")
final def scalaStyledName: String = this.scalaStyledReprImpl
final def shortName: String = this.shortNameImpl
final def longNameWithPrefix: String = this.longNameWithPrefixImpl
final def longNameInternalSymbol: String = this.longNameInternalSymbolImpl
@deprecated(
"Produces Scala version dependent output, with incorrect prefixes for types with value prefixes. Use `longNameWithPrefix` instead, or `longNameInternalSymbol` for old behavior",
"2.2.2"
)
final def longName: String = this.longNameInternalSymbolImpl
final def getPrefix: Option[AppliedReference] = this.getPrefixImpl
final def typeArgs: List[AbstractReference] = this.typeArgsImpl
/** decompose intersection type */
final def decompose: Set[LightTypeTagRef.AppliedReferenceExceptIntersection] = this.decomposeImpl
final def decomposeUnion: Set[LightTypeTagRef.AppliedReferenceExceptUnion] = this.decomposeUnionImpl
}
object LightTypeTagRef extends LTTOrdering {
import LTTRenderables.Short._
// import LTTRenderables.Long._
sealed trait AbstractReference extends LightTypeTagRef
// bincompat only
private[macrortti] sealed abstract class LambdaParameter {
@deprecated("bincompat only", "20.02.2023")
private[macrortti] def name: String
}
@deprecated("bincompat only", "20.02.2023")
private[macrortti] object LambdaParameter extends (String => LambdaParameter) {
@deprecated("bincompat only", "20.02.2023")
def apply(name: String): LambdaParameter = {
SymName.bincompatForceCreateLambdaParamNameFromString(name)
}
}
final case class Lambda(input: List[SymName.LambdaParamName], output: AbstractReference) extends AbstractReference {
override def hashCode(): Int = {
normalizedOutput.hashCode()
}
lazy val inputSize: Int = input.size
lazy val paramRefs: Set[NameReference] = input
.iterator.map {
n =>
// No boundary on paramRefs
// FIXME LambdaParameter should contain bounds and NameReference shouldn't
// (Except possibly lower bound of an abstract/opaque type member)
NameReference(n)
}.toSet
lazy val referenced: Set[NameReference] = RuntimeAPI.unpack(this)
def allArgumentsReferenced: Boolean = paramRefs.diff(referenced).isEmpty
lazy val someArgumentsReferenced: Boolean = {
val unusedParamsSize = paramRefs.diff(referenced).size
unusedParamsSize < paramRefs.size
}
lazy val normalizedParams: List[NameReference] = makeFakeParams.map(_._2)
lazy val normalizedOutput: AbstractReference = RuntimeAPI.applyLambda(this, makeFakeParams)
override def equals(obj: Any): Boolean = {
obj match {
case l: Lambda =>
inputSize == l.inputSize &&
(normalizedOutput == l.normalizedOutput)
case _ =>
false
}
}
private[this] def makeFakeParams: List[(LambdaParamName, NameReference)] = {
input.zipWithIndex.map {
case (p, idx) =>
p -> NameReference(SymName.LambdaParamName(idx, LightTypeTagRef.LambdaConstants.lambdaFakeParamDepth, inputSize)) // s"!FAKE_$idx"
}
}
}
object LambdaConstants {
final val defaultContextId = -1
final val lambdaFakeParamDepth: Int = -2 // depth is always positive, unless fake
final val tagMacro = -3
}
sealed trait AppliedReference extends AbstractReference
sealed trait AppliedReferenceExceptIntersection extends AppliedReference
sealed trait AppliedReferenceExceptUnion extends AppliedReference
final case class IntersectionReference(refs: Set[AppliedReferenceExceptIntersection]) extends AppliedReferenceExceptUnion {
override lazy val hashCode: Int = scala.runtime.ScalaRunTime._hashCode(this)
}
final case class WildcardReference(boundaries: Boundaries) extends AppliedReferenceExceptIntersection with AppliedReferenceExceptUnion
final case class UnionReference(refs: Set[AppliedReferenceExceptUnion]) extends AppliedReferenceExceptIntersection {
override lazy val hashCode: Int = scala.runtime.ScalaRunTime._hashCode(this)
}
final case class Refinement(reference: AppliedReference, decls: Set[RefinementDecl]) extends AppliedReferenceExceptIntersection with AppliedReferenceExceptUnion {
override lazy val hashCode: Int = scala.runtime.ScalaRunTime._hashCode(this)
}
def isIgnored[T <: AbstractReference](t: T): Boolean = {
t match {
case a: AppliedReference =>
ignored.contains(a)
case _: Lambda =>
false
}
}
private[reflect] def ignored[T >: NameReference]: Set[T] = ignored0.asInstanceOf[Set[T]]
private[this] val ignored0: Set[NameReference] = Set[NameReference](
LightTypeTagInheritance.tpeAny,
LightTypeTagInheritance.tpeMatchable,
LightTypeTagInheritance.tpeAnyRef,
LightTypeTagInheritance.tpeObject
)
def maybeIntersection(refs0: Iterator[_ <: LightTypeTagRef]): AppliedReference = {
val refs = refs0.flatMap(_.decompose).toSet // flatten nested intersections
if (refs.size == 1) {
refs.head
} else {
val normalized = refs.diff(ignored)
if (normalized.isEmpty) {
LightTypeTagInheritance.tpeAny
} else if (normalized.size == 1) {
normalized.head
} else {
IntersectionReference(normalized)
}
}
}
def maybeUnion(refs0: Iterator[_ <: LightTypeTagRef]): AppliedReference = {
val refs = refs0.flatMap(_.decomposeUnion).toSet // flatten nested unions
val normalized = refs -- Set(LightTypeTagInheritance.tpeNothing, LightTypeTagInheritance.tpeNull)
val superTypes = normalized.intersect(ignored)
if (superTypes.nonEmpty) {
if (normalized.contains(LightTypeTagInheritance.tpeAny)) LightTypeTagInheritance.tpeAny
else if (normalized.contains(LightTypeTagInheritance.tpeMatchable)) LightTypeTagInheritance.tpeMatchable
else if (normalized.contains(LightTypeTagInheritance.tpeAnyRef)) LightTypeTagInheritance.tpeAnyRef
else if (normalized.contains(LightTypeTagInheritance.tpeObject)) LightTypeTagInheritance.tpeObject
else superTypes.head
} else {
if (normalized.isEmpty) {
LightTypeTagInheritance.tpeNothing
} else if (normalized.size == 1) {
normalized.head
} else {
UnionReference(normalized)
}
}
}
def maybeIntersection(r: Set[_ <: LightTypeTagRef]): AppliedReference = maybeIntersection(r.iterator)
def maybeUnion(r: Set[_ <: LightTypeTagRef]): AppliedReference = maybeUnion(r.iterator)
sealed trait AppliedNamedReference extends AppliedReferenceExceptIntersection with AppliedReferenceExceptUnion {
def asName: NameReference
def symName: SymName
def prefix: Option[AppliedReference]
}
final case class NameReference(
ref: SymName,
boundaries: Boundaries = Boundaries.Empty, // Quirk, we only need it to use NameReferences as Lambda parameters
prefix: Option[AppliedReference] = None
) extends AppliedNamedReference {
override lazy val hashCode: Int = scala.runtime.ScalaRunTime._hashCode(this)
override def asName: NameReference = this
override def symName: SymName = ref
}
object NameReference {
@deprecated("Use SymName explicitly", "20.02.2023")
private[NameReference] def apply(tpeName: String): NameReference = NameReference(SymTypeName(tpeName))
}
final case class FullReference(
symName: SymName,
parameters: List[TypeParam],
prefix: Option[AppliedReference] = None
) extends AppliedNamedReference {
override lazy val hashCode: Int = scala.runtime.ScalaRunTime._hashCode(this)
override def asName: NameReference = NameReference(symName, boundaries = Boundaries.Empty, prefix = prefix)
@deprecated("bincompat only", "20.02.2023")
private[LightTypeTagRef] def ref: String = SymName.forceName(symName)
@deprecated("bincompat only", "20.02.2023")
private[LightTypeTagRef] def this(ref: String, parameters: List[TypeParam], prefix: Option[AppliedReference]) = {
this(SymName.SymTypeName(ref), parameters, prefix)
}
@deprecated("bincompat only", "20.02.2023")
private[LightTypeTagRef] def copy(ref: String, parameters: List[TypeParam], prefix: Option[AppliedReference]): FullReference = {
this.copy(SymName.SymTypeName(ref), parameters, prefix)
}
@deprecated("bincompat only", "20.02.2023")
private[LightTypeTagRef] def copy$default$1: String = ref
def copy(symName: SymName = symName, parameters: List[TypeParam] = parameters, prefix: Option[AppliedReference] = prefix): FullReference = {
new FullReference(symName, parameters, prefix)
}
}
object FullReference extends /*bincompat*/ AbstractFunction3[String, List[TypeParam], Option[AppliedReference], FullReference] {
@deprecated("bincompat only", "20.02.2023")
override def apply(ref: String, parameters: List[TypeParam], prefix: Option[AppliedReference]): FullReference = {
new FullReference(SymName.SymTypeName(ref), parameters, prefix)
}
}
final case class TypeParam(
ref: AbstractReference,
variance: Variance // Quirk, we only need it to simplify/speedup inheritance checks
) {
override def toString: String = this.render()
}
sealed trait RefinementDecl {
def name: String
}
object RefinementDecl {
final case class Signature(name: String, input: List[AppliedReference], output: AppliedReference) extends RefinementDecl
final case class TypeMember(name: String, ref: AbstractReference) extends RefinementDecl
}
sealed trait Variance {
override final def toString: String = this.render()
}
object Variance {
case object Invariant extends Variance
case object Contravariant extends Variance
case object Covariant extends Variance
}
sealed trait Boundaries {
override final def toString: String = this.render()
}
object Boundaries {
final case class Defined(bottom: AbstractReference, top: AbstractReference) extends Boundaries
case object Empty extends Boundaries
}
// this name isn't correct anymore but we keep it here for historical reasons. In fact that should be Symbol or SymRef
sealed trait SymName {
// bincompat only
private[macrortti] def name: String
}
object SymName {
final case class LambdaParamName(index: Int, depth: Int, arity: Int) extends LambdaParameter with SymName {
@deprecated("bincompat only", "20.02.2023")
private[macrortti] override def name: String = SymName.forceName(this)
}
sealed trait NamedSymbol extends SymName {
def name: String
}
final case class SymTermName(name: String) extends NamedSymbol
final case class SymTypeName(name: String) extends NamedSymbol
final case class SymLiteral(name: String) extends NamedSymbol
object SymLiteral {
def apply(c: Any): SymLiteral = {
val constant = c match {
case s: String => "\"" + s + "\""
case o => o.toString
}
SymLiteral(constant)
}
}
implicit final class SymNameExt(private val name: SymName) extends AnyVal {
def maybeName: Option[String] = name match {
case symbol: SymName.NamedSymbol => Some(symbol.name)
case _: SymName.LambdaParamName => None
}
}
private[macrortti] def forceName(symName: SymName): String = {
symName match {
case symbol: SymName.NamedSymbol => symbol.name
case lpn: LambdaParamName => LTTRenderables.Long.r_LambdaParameterName.render(lpn)
}
}
private[macrortti] def bincompatForceCreateLambdaParamNameFromString(str: String): LambdaParamName = {
val (numericIndexFromString, numericContextFromString) = {
val parts = str.split(':')
val fst = Try(parts(0).toInt)
val snd = Try(parts(1).toInt)
(fst, snd) match {
case (Success(idx), Failure(_)) =>
(idx, -10)
case (Success(ctx), Success(idx)) =>
(idx, ctx)
case _ =>
// use MurmurHash as it promises 'high-quality'
(MurmurHash3.stringHash(str), -10)
}
}
LambdaParamName(numericIndexFromString, numericContextFromString, -10)
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala/izumi/reflect/macrortti/RuntimeAPI.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.macrortti
import izumi.reflect.internal.NowarnCompat
import izumi.reflect.internal.fundamentals.platform.language.unused
import izumi.reflect.macrortti.LightTypeTagRef._
object RuntimeAPI {
def unpack(ref: AbstractReference): Set[NameReference] = {
def unpackBoundaries(b: Boundaries): Set[NameReference] = {
b match {
case Boundaries.Defined(bottom, top) =>
unpack(bottom) ++ unpack(top)
case Boundaries.Empty =>
Set.empty
}
}
ref match {
case Lambda(_, output) =>
unpack(output)
case reference: AppliedReference =>
reference match {
case reference: AppliedNamedReference =>
reference match {
case n: NameReference =>
Set(n.copy(prefix = None, boundaries = Boundaries.Empty)) ++ n.prefix.toSet.flatMap(unpack) ++ unpackBoundaries(n.boundaries)
case f: FullReference =>
f.parameters.iterator.map(_.ref).flatMap(unpack).toSet ++ f.prefix.toSet.flatMap(unpack) + f.asName
}
case _: WildcardReference =>
Set.empty
case IntersectionReference(refs) =>
refs.flatMap(unpack)
case UnionReference(refs) =>
refs.flatMap(unpack)
case Refinement(reference, decls) =>
unpack(reference) ++ decls.flatMap(
d =>
d match {
case RefinementDecl.Signature(_, input, output) =>
unpack(output) ++ input.flatMap(unpack)
case RefinementDecl.TypeMember(_, ref) =>
unpack(ref)
}
)
}
}
}
def applyLambda(lambda: Lambda, parameters: Seq[(SymName.LambdaParamName, AbstractReference)]): AbstractReference = {
val paramMap = parameters.toMap
val rewriter = new Rewriter(paramMap)
val replaced = rewriter.replaceRefs(lambda.output)
// fix for #82, #83
// the old logic was ill, it was causing incorrect replacements when names from substitutions were clashing with replacement map
// in fact that was a broken workaround for the lack of recursion in Rewriter
// val replaced = parameters.foldLeft(lambda.output) {
// case (acc, p) =>
// val rewriter = new Rewriter(Map(p))
// rewriter.replaceRefs(acc)
// }
val newParams = lambda.input.filterNot(paramMap.contains)
val res = if (newParams.isEmpty) {
replaced
} else {
val out = Lambda(newParams, replaced)
// assert(out.allArgumentsReferenced, s"bad lambda: $out, ${out.paramRefs}, ${out.referenced}")
// such lambdas are legal: see "regression test: combine Const Lambda to TagK"
out
}
res
}
final class Rewriter(_rules: Map[SymName.LambdaParamName, AbstractReference]) {
private val rules: Map[SymName, AbstractReference] = _rules.toMap
def complete(@unused context: AppliedNamedReference, ref: AbstractReference): AbstractReference = {
ref
}
@NowarnCompat.nowarn("msg=deprecated")
def replaceRefs(reference: AbstractReference): AbstractReference = {
reference match {
case l: Lambda =>
val bad = l.input.iterator.toSet
val fixed = new Rewriter(_rules.filterKeys(!bad.contains(_)).iterator.toMap).replaceRefs(l.output)
l.copy(output = fixed)
case o: AppliedReference =>
replaceApplied(o)
}
}
def replacePrefix(prefix: Option[AppliedReference]): Option[AppliedReference] = {
prefix.map(p => ensureApplied(p, replaceApplied(p)))
}
def replaceBoundaries(boundaries: Boundaries): Boundaries = {
boundaries match {
case Boundaries.Defined(bottom, top) =>
Boundaries.Defined(replaceRefs(bottom), replaceRefs(top))
case Boundaries.Empty =>
boundaries
}
}
private def replaceApplied(reference: AppliedReference): AbstractReference = {
reference match {
case IntersectionReference(refs) =>
val replaced = refs.map(replaceApplied).map(ensureApplied(reference, _))
maybeIntersection(replaced)
case UnionReference(refs) =>
val replaced = refs.map(replaceApplied).map(ensureApplied(reference, _))
maybeUnion(replaced)
case WildcardReference(boundaries) =>
WildcardReference(replaceBoundaries(boundaries))
case Refinement(base, decls) =>
val rdecls = decls.map {
case RefinementDecl.Signature(name, input, output) =>
RefinementDecl.Signature(name, input.map(p => ensureApplied(reference, replaceRefs(p))), ensureApplied(reference, replaceRefs(output))): RefinementDecl
case RefinementDecl.TypeMember(name, ref) =>
RefinementDecl.TypeMember(name, replaceRefs(ref)): RefinementDecl
}
Refinement(ensureApplied(base, replaceApplied(base)), rdecls)
case n: AppliedNamedReference =>
replaceNamed(n)
}
}
private def replaceNamed(reference: AppliedNamedReference): AbstractReference = {
def returnFullRef(fixedRef: SymName, parameters: List[TypeParam], prefix: Option[AppliedReference]): FullReference = {
val p = parameters.map {
case TypeParam(pref, variance) =>
TypeParam(replaceRefs(pref), variance)
}
FullReference(fixedRef, p, prefix)
}
reference match {
case n @ NameReference(ref, boundaries, prefix) =>
rules.get(ref) match {
case Some(value) =>
complete(n, value)
case None =>
NameReference(ref, replaceBoundaries(boundaries), replacePrefix(prefix))
}
case f @ FullReference(ref, parameters, prefix) =>
rules.get(ref) match {
case Some(value) =>
complete(f, value) match {
case out: Lambda =>
val refs = parameters.map(_.ref)
val res = replaceRefs(out.applySeq(refs)) // fix for #82, #83
res
case n: NameReference =>
// we need this to support fakes only (see LightTypeTagRef#makeFakeParams)
returnFullRef(n.ref, parameters, prefix)
case out =>
throw new IllegalStateException(s"Lambda expected for context-bound $f, but got $out")
}
case None =>
returnFullRef(ref, parameters, prefix)
}
}
}
private def ensureApplied(context: AbstractReference, ref: AbstractReference): AppliedReference = {
ref match {
case reference: AppliedReference =>
reference
case o =>
throw new IllegalStateException(s"Expected applied reference but got $o while processing $context")
}
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/AnnotationTools.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect
import scala.reflect.api.Universe
private[reflect] object AnnotationTools {
def findArgument[A](ann: Universe#Annotation)(matcher: PartialFunction[Universe#Tree, A]): Option[A] =
ann.tree.children.tail.collectFirst(matcher)
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/ReflectionUtil.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect
import izumi.reflect.internal.fundamentals.platform.console.TrivialLogger
import scala.annotation.tailrec
import scala.reflect.api.Universe
private[reflect] object ReflectionUtil {
/** Mini `normalize`. `normalize` is deprecated and we don't want to do scary things such as evaluate type-lambdas anyway.
* And AFAIK the only case that can make us confuse a type-parameter for a non-parameter is an empty refinement `T {}`.
* So we just strip it when we get it.
*/
// ReflectionUtil.norm but with added logging
@tailrec @inline
def norm(u: Universe, logger: TrivialLogger)(x: u.Type): u.Type = {
import u._
x match {
case RefinedType(t :: Nil, m) if m.isEmpty =>
logger.log(s"Stripped empty refinement of type $t. member scope $m")
norm(u, logger)(t)
case AnnotatedType(_, t) =>
norm(u, logger)(t)
case _ =>
x
}
}
def allPartsStrong(tpe: Universe#Type): Boolean = {
allPartsStrong(Set.empty, tpe)
}
def allPartsStrong(outerTypeParams: Set[Universe#Symbol], tpeRaw: Universe#Type): Boolean = {
val dealiased = tpeRaw.dealias
val selfStrong = isSelfStrong(outerTypeParams, dealiased) || outerTypeParams.contains(dealiased.typeSymbol)
dealiased match {
case t: Universe#RefinedTypeApi =>
t.parents.forall(allPartsStrong(outerTypeParams, _)) &&
t.decls.toSeq.forall((s: Universe#Symbol) => s.isTerm || allPartsStrong(outerTypeParams, s.asType.typeSignature.dealias))
case e: Universe#ExistentialTypeApi =>
allPartsStrong(outerTypeParams, e.underlying) &&
e.underlying.typeArgs.forall(
t =>
t.typeSymbol.typeSignature match {
case tb: Universe#TypeBoundsApi => allPartsStrong(outerTypeParams, tb.hi) && allPartsStrong(outerTypeParams, tb.lo)
case _ => allPartsStrong(outerTypeParams, t)
}
) &&
e.quantified.forall((s: Universe#Symbol) => allPartsStrong(outerTypeParams, s.asType.typeSignature.dealias))
case _ =>
val resType = dealiased.finalResultType
if (dealiased.takesTypeArgs && !dealiased.typeParams.forall(outerTypeParams.contains)) {
allPartsStrong(outerTypeParams ++ dealiased.typeParams, resType)
} else {
def typeCtorStrong: Boolean = {
val ctor = resType.typeConstructor
(resType == dealiased) || (ctor == dealiased) || (ctor == tpeRaw) ||
outerTypeParams.contains(ctor.typeSymbol) || allPartsStrong(outerTypeParams, ctor)
}
def argsStrong: Boolean = {
resType.typeArgs.forall {
arg =>
outerTypeParams.contains(arg.typeSymbol) || allPartsStrong(outerTypeParams, arg)
}
}
selfStrong && /*prefixStrong &&*/ typeCtorStrong && argsStrong
}
}
}
def isSelfStrong(outerTypeParams: Set[Universe#Symbol], tpe: Universe#Type): Boolean = {
// FIXME: strengthening check to capture `IntersectionBlockingIO` test case causes StackOverflow during implicit search
// def intersectionMembersStrong = {
// tpe match {
// case t: Universe#RefinedTypeApi =>
// t.parents.forall(isSelfStrong)
// case _ => true
// }
// }
def prefixStrong: Boolean = {
tpe match {
case t: Universe#TypeRefApi =>
allPartsStrong(outerTypeParams, t.pre.dealias)
case _ =>
true
}
}
(prefixStrong && !(tpe.typeSymbol.isParameter || (
// we regard abstract types like T in trait X { type T; Tag[this.T] } - when we are _inside_ the definition template
// as 'type parameters' too. So that you could define `implicit def tagForT: Tag[this.T]` and the tag would be resolved
// to this implicit correctly, instead of generating a useless `X::this.type::T` tag.
isAbstractType(tpe) &&
tpe.asInstanceOf[Universe#TypeRefApi].pre.isInstanceOf[Universe#ThisTypeApi]
))) /*&& intersectionMembersStrong*/ ||
isIdentityLikeTypeLambda(tpe)
}
def isAbstractType(tpe: Universe#Type): Boolean = {
tpe.isInstanceOf[Universe#TypeRefApi] && tpe.typeSymbol.isAbstract && !tpe.typeSymbol.isClass && isNotDealiasedFurther(tpe)
}
def isIdentityLikeTypeLambda(tpe: Universe#Type): Boolean = {
tpe.typeParams.exists { // is identity
t =>
t == tpe.typeSymbol ||
t.typeSignature == tpe.typeSymbol.typeSignature ||
(t.name eq tpe.typeSymbol.name)
}
}
def isNotDealiasedFurther(tpe: Universe#Type): Boolean = {
val u: Universe = null
val tpe1: u.Type = tpe.asInstanceOf[u.Type]
tpe1.dealias =:= tpe1
}
/** Represents the kind of a type, including bounds.
* @param params nested kinds for each type parameter
* @param bounds optional type bounds (None for default bounds Nothing..Any)
* @param symbol original type parameter symbol (for substitution during type construction)
*/
final case class KindInfo[+U <: Universe](params: List[KindInfo[U]], bounds: Option[U#TypeBoundsApi], symbol: U#Symbol)
object KindInfo {
def ofType[U <: Universe with Singleton](tpe: U#Type): KindInfo[U] = {
KindInfo.of(tpe, tpe.typeSymbol)
}
def ofSymbol[U <: Universe with Singleton](sym: U#Symbol): KindInfo[U] = {
KindInfo.of(sym.typeSignature, sym)
}
def of[U <: Universe with Singleton](tpe: U#Type, sym: U#Symbol): KindInfo[U] = {
val bounds: Option[U#TypeBoundsApi] = tpe match {
case tb: U#TypeBoundsApi => Some(tb)
case pt: U#PolyTypeApi =>
pt.resultType match {
case tb: U#TypeBoundsApi => Some(tb)
case _ => None
}
case _ => None
}
KindInfo[U](tpe.typeParams.map(KindInfo.ofSymbol), bounds, sym)
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/ScalacSink.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect
import izumi.reflect.internal.fundamentals.platform.console.AbstractStringTrivialSink
import scala.reflect.macros.blackbox
private[reflect] final class ScalacSink(c: blackbox.Context) extends AbstractStringTrivialSink {
override def flush(value: => String): Unit = {
c.info(c.enclosingPosition, value, force = true)
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/TagMacro.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect
import izumi.reflect.ReflectionUtil.KindInfo
import izumi.reflect.TagMacro._
import izumi.reflect.internal.NowarnCompat
import izumi.reflect.internal.fundamentals.platform.console.TrivialLogger
import izumi.reflect.macrortti.LightTypeTagRef._
import izumi.reflect.macrortti.{LightTypeTag, LightTypeTagMacro0, LightTypeTagRef}
import scala.annotation.implicitNotFound
import scala.collection.immutable.ListMap
import scala.reflect.api.Universe
import scala.reflect.macros.{TypecheckException, blackbox, whitebox}
// TODO: benchmark difference between searching all arguments vs. merge strategy
// TODO: benchmark ProviderMagnet vs. identity macro vs. normal function
@NowarnCompat.nowarn("msg=deprecated")
class TagMacro(val c: blackbox.Context) {
import c.universe._
protected[this] val logger: TrivialLogger = TrivialMacroLogger.make[this.type](c)
private[this] val ltagMacro = new LightTypeTagMacro0[c.type](c)(logger)
// workaround for a scalac bug - `Nothing` type is lost when two implicits for it are summoned from one implicit as in:
// implicit final def tagFromTypeTag[T](implicit t: TypeTag[T], l: LTag[T]): Tag[T] = Tag(t, l.fullLightTypeTag)
// https://github.com/scala/bug/issues/11715
final def makeTag[T: c.WeakTypeTag]: c.Expr[Tag[T]] = {
val tpe = weakTypeOf[T]
if (ReflectionUtil.allPartsStrong(tpe.dealias)) {
makeStrongTagImpl[T](tpe, implicitly[c.WeakTypeTag[T]])
} else {
makeWeakTagImpl[T](tpe, implicitly[c.WeakTypeTag[T]])
}
}
final def makeStrongTag[T: c.WeakTypeTag]: c.Expr[Tag[T]] = {
val tpe = weakTypeOf[T]
assert(ReflectionUtil.allPartsStrong(tpe.dealias))
makeStrongTagImpl[T](tpe, implicitly[c.WeakTypeTag[T]])
}
final def makeWeakTag[T: c.WeakTypeTag]: c.Expr[Tag[T]] = {
val tpe = weakTypeOf[T]
assert(!ReflectionUtil.allPartsStrong(tpe.dealias))
makeWeakTagImpl[T](tpe, implicitly[c.WeakTypeTag[T]])
}
@inline final def makeHKTagMaterializer[ArgStruct: c.WeakTypeTag]: c.Expr[HKTagMaterializer[ArgStruct]] = {
c.Expr[HKTagMaterializer[ArgStruct]](q"new ${weakTypeOf[HKTagMaterializer[ArgStruct]]}(${makeHKTag[ArgStruct]})")
}
@inline final def makeHKTag[ArgStruct](implicit argStructTag: c.WeakTypeTag[ArgStruct]): c.Expr[HKTag[ArgStruct]] = {
val argStruct = weakTypeOf[ArgStruct]
val ctor = ltagMacro.unpackArgStruct(argStruct)
if (ReflectionUtil.allPartsStrong(ctor)) {
logger.log(s"HK: found Strong ctor=$ctor in ArgStruct, returning $argStruct")
makeHKTagFromStrongTpe[ArgStruct](ctor)
} else {
makeHKTagImpl(ctor, argStructTag)
}
}
private def makeStrongTagImpl[T](tpe: c.Type, tag: c.WeakTypeTag[T]): c.Expr[Tag[T]] = {
logger.log(s"Got strong tag, generating LTT right away: ${tag.tpe}")
val ltag = ltagMacro.makeParsedLightTypeTagImpl(tpe)
val cls = closestClass(tpe)
{
// compiler always inserts WeakTypeTag, by passing it explicitly we slightly reduce fragility
implicit val itag: c.WeakTypeTag[T] = tag
c.Expr[Tag[T]] {
q"_root_.izumi.reflect.Tag.apply[$tpe]($cls, $ltag)"
}
}
}
private def makeWeakTagImpl[T](tpe: c.Type, tag: c.WeakTypeTag[T]): c.Expr[Tag[T]] = {
if (getImplicitError().endsWith(":")) { // yep
logger.log(s"Got continuation implicit error: ${getImplicitError()}")
} else {
resetImplicitError(tpe)
addImplicitError("\n\n: ")
}
val tgt = ReflectionUtil.norm(c.universe: c.universe.type, logger)(tpe.dealias)
logger.log(s"Got non-strong tag: $tpe, dealiased: $tgt")
addImplicitError(s" deriving Tag for $tpe, dealiased: $tgt:")
val res = tgt match {
case RefinedType(intersection, _) =>
mkRefined[T](intersection, tgt, tag)
case _ =>
mkTagWithTypeParameters[T](tgt, tag)
}
addImplicitError(s" succeeded for: $tgt")
logger.log(s"Final code of Tag[$tpe] (dealiased $tgt):\n ${showCode(res.tree)}")
res
}
// FIXME: nearly a copypaste of mkTagWithTypeParameters, deduplicate?
private[this] def makeHKTagImpl[ArgStruct](outerLambda: Type, tag: c.WeakTypeTag[ArgStruct]): c.Expr[HKTag[ArgStruct]] = {
logger.log(s"Got unresolved HKTag summon: ${tagFormat(outerLambda)} from ArgStruct: ${tag.tpe}")
def isLambdaParamOf(arg: Type, lam: Type): Boolean = {
lam.typeParams.contains(arg.typeSymbol)
}
val lambdaResult = outerLambda.finalResultType
val ctorTpe = lambdaResult.typeConstructor
val typeArgsTpes = lambdaResult.typeArgs
val isSimplePartialApplication = {
// all parameters are consumed exactly once, in left-to-right order,
// but there could be type arguments applied to the left of parameters
typeArgsTpes
.map(_.typeSymbol)
.filter(outerLambda.typeParams.contains) == outerLambda.typeParams
}
val constructorTag: c.Expr[HKTag[_]] = {
// outer lambda is just a forwarding lambda `[A, B, C] =>> F[A, B, C]`, exactly equivalent to `F` itself
val outerLambdaEqualsCtorEtaExpand = {
isSimplePartialApplication &&
typeArgsTpes.corresponds(outerLambda.typeParams)(_.typeSymbol == _)
}
getCtorKindInfoIfCtorIsTypeParameter(ctorTpe) match {
// type constructor of this type is not a type parameter
// BUT can be an intersection type
// some of its arguments are type parameters that we should resolve
case None =>
logger.log(s"HK type A ctor=$ctorTpe sym=${ctorTpe.typeSymbol}")
makeHKTagFromStrongTpe[Any](ctorTpe)
// outerLambda is a simple forwarder `[A, B, C] =>> F[A, B, C]` AND `F` is a type parameter
// We can't progress here: we must find HKTag[F] to progress, but we ARE in the process of
// deriving HKTag[F], which means it doesn't already exist.
case Some(_) if outerLambdaEqualsCtorEtaExpand =>
logger.log(s"HK type B (error) $ctorTpe ${ctorTpe.typeSymbol} - can't find tag for ctor=${showRaw(ctorTpe)}, etaExpand=${showRaw(outerLambda)}")
val msg = s" could not find implicit value for ${tagFormat(lambdaResult)}: $lambdaResult is a type parameter without an implicit Tag!"
addImplicitError(msg)
abortWithImplicitError()
// type constructor is a type parameter AND has type arguments
// we should resolve type constructor separately from an HKTag
case Some(hktKind) =>
logger.log(s"HK type C $ctorTpe ${ctorTpe.typeSymbol}")
summonHKTag(ctorTpe, hktKind)
}
}
val res = {
if (isSimplePartialApplication) {
val embeddedMaybeNonParamTypeArgs = typeArgsTpes.map {
arg => if (!isLambdaParamOf(arg, outerLambda)) Some(arg) else None
}
val argTags = {
// FIXME: create LTT in-place instead of summoning Tag if typeArg is Strong
logger.log(s"HK Now summoning tags for args=$embeddedMaybeNonParamTypeArgs")
val argExprs = embeddedMaybeNonParamTypeArgs.map(_.map {
t =>
val dealiased = ReflectionUtil.norm(c.universe: c.universe.type, logger)(t.dealias)
summonLightTypeTagOfAppropriateKind(dealiased)
})
c.Expr[List[Option[LightTypeTag]]](q"$argExprs")
}
{
// compiler always inserts WeakTypeTag, by passing it explicitly we slightly reduce fragility. quasiquotes implicitly use WTT
implicit val itag: c.WeakTypeTag[ArgStruct] = tag
reify {
HKTag.appliedTagNonPos[ArgStruct](constructorTag.splice, argTags.splice)
}
}
} else {
// warn if constructor is an intersection type, as they don't work properly in this position right now
lambdaResult match {
case t: RefinedTypeApi if t.parents.size > 1 =>
// info instead of warning because warnings are suppressed by Scala 2 inside implicit macros for some reason
c.info(
c.enclosingPosition,
s"""TODO: Pathological intersection refinement result in lambda being reconstructed result=`$lambdaResult` in the rhs of type lambda lam=`$outerLambda`
|Only simple applied types of form F[A] are supported in results of type lambdas. The generated tag may not work correctly.""".stripMargin,
force = true
)
case _ =>
}
val PolyType(outerLambdaParamArgsSyms, _) = outerLambda: @unchecked
val distinctNonParamArgsTypes = typeArgsTpes.filter(!isLambdaParamOf(_, outerLambda)).distinct
val arity = distinctNonParamArgsTypes.size + outerLambdaParamArgsSyms.size + 1
val typeArgToLambdaParameterMap: Map[Either[Type, Symbol], SymName.LambdaParamName] =
// for non-lambda arguments the types are unique, but symbols are not, for lambda arguments the symbols are unique but types are not.
// it's very confusing.
(distinctNonParamArgsTypes.map(Left(_)) ++ outerLambdaParamArgsSyms.map(Right(_)))
.distinct.iterator.zipWithIndex.map {
case (argTpeOrSym, idx) =>
val idxPlusOne = idx + 1
val lambdaParameter = SymName.LambdaParamName(idxPlusOne, LightTypeTagRef.LambdaConstants.tagMacro, arity)
argTpeOrSym -> lambdaParameter
}.toMap
def getFromMap(k1: Either[Type, Symbol], k2: Either[Type, Symbol]): SymName.LambdaParamName = {
typeArgToLambdaParameterMap.getOrElse(
k1,
typeArgToLambdaParameterMap.getOrElse(
k2, {
val msg = s"Problem: couldn't get a lambda parameter idx for k1=$k1 k2=$k2 in $typeArgToLambdaParameterMap"
logger.log(msg)
c.abort(c.enclosingPosition, msg)
}
)
)
}
val usageOrderDistinctNonLambdaArgs = distinctNonParamArgsTypes.map(t => getFromMap(Left(t), Right(t.typeSymbol)))
val declarationOrderLambdaParamArgs = outerLambdaParamArgsSyms.map(sym => getFromMap(Right(sym), Left(sym.typeSignature)))
val usages = typeArgsTpes.map(t => TypeParam(NameReference(getFromMap(Left(t), Right(t.typeSymbol))), Variance.Invariant))
// we give a distinct lambda parameter to the constructor, even if constructor is one of the type parameters
val firstParamIdx = 0
val ctorLambdaParameter = SymName.LambdaParamName(firstParamIdx, LightTypeTagRef.LambdaConstants.tagMacro, arity)
val ctorApplyingLambda = LightTypeTagRef.Lambda(
ctorLambdaParameter :: usageOrderDistinctNonLambdaArgs ::: declarationOrderLambdaParamArgs,
FullReference(ctorLambdaParameter, usages)
)
logger.log(s"""HK non-trivial lambda construction:
|ctorApplyingLambda=$ctorApplyingLambda
|usageOrderNonLambdaArgs=$usageOrderDistinctNonLambdaArgs
|declarationOrderLambdaParamArgs=$declarationOrderLambdaParamArgs
|""".stripMargin)
val argTagsExceptCtor = {
val nonParamArgsDealiased = distinctNonParamArgsTypes.map(t => ReflectionUtil.norm(c.universe: c.universe.type, logger)(t.dealias))
logger.log(s"HK COMPLEX Now summoning tags for args=$nonParamArgsDealiased outerLambdaParams=$outerLambdaParamArgsSyms")
c.Expr[List[Option[LightTypeTag]]] {
q"${nonParamArgsDealiased.map(t => Some(summonLightTypeTagOfAppropriateKind(t))) ++ outerLambdaParamArgsSyms.map(_ => None)}"
}
}
val outerLambdaReprTag = ltagMacro.makeParsedLightTypeTagImpl(LightTypeTag(ctorApplyingLambda, Map.empty, Map.empty))
{
// compiler always inserts WeakTypeTag, by passing it explicitly we slightly reduce fragility. quasiquotes implicitly use WTT
implicit val itag: c.WeakTypeTag[ArgStruct] = tag
reify {
val ctorTag = constructorTag.splice
HKTag.appliedTagNonPosAux[ArgStruct](ctorTag.closestClass, outerLambdaReprTag.splice, Some(ctorTag.tag) :: argTagsExceptCtor.splice)
}
}
}
}
logger.log(s"Final code of ${tagFormat(outerLambda)}:\n ${showCode(res.tree)}")
res
}
private[this] def makeHKTagFromStrongTpe[ArgStruct: c.WeakTypeTag](strongCtorType: Type): c.Expr[HKTag[ArgStruct]] = {
val ltag = ltagMacro.makeParsedLightTypeTagImpl(strongCtorType)
val cls = closestClass(strongCtorType)
c.Expr[HKTag[ArgStruct]] {
q"_root_.izumi.reflect.HKTag.apply($cls, $ltag)"
}
}
@inline
protected[this] def mkRefined[T](intersection: List[Type], originalRefinement: Type, tag: c.WeakTypeTag[T]): c.Expr[Tag[T]] = {
val summonedIntersectionTags = intersection.map {
t0 =>
val t = ReflectionUtil.norm(c.universe: c.universe.type, logger)(t0.dealias)
summonLightTypeTagOfAppropriateKind(t)
}
val intersectionTags = c.Expr[List[LightTypeTag]](Liftable.liftList[c.Expr[LightTypeTag]].apply(summonedIntersectionTags))
val (structTag, additionalTypeMembers) = mkStruct(intersection, originalRefinement)
val cls = closestClass(originalRefinement)
{
// compiler always inserts WeakTypeTag, by passing it explicitly we slightly reduce fragility
implicit val itag: c.WeakTypeTag[T] = tag
reify {
Tag.refinedTag[T](cls.splice, intersectionTags.splice, structTag.splice, additionalTypeMembers.splice)
}
}
}
@inline
protected[this] def mkStruct(intersection: List[Type], originalRefinement: Type): (c.Expr[LightTypeTag], c.Expr[Map[String, LightTypeTag]]) = {
val (strongDecls, weakDecls) = originalRefinement
.decls
.partition {
symbol =>
// skip resolution for types in methods/vals (that would need a new runtime constructor, `methodTag`, like `refinedTag` for the case & dealing with method type parameters may be non-trivial)
// also skip resolution for "strong" type members
// see: "progression test: can't handle parameters in defs/vals in structural types"
symbol.isTerm ||
ReflectionUtil.allPartsStrong(Set.empty, symbol.info)
}
val strongDeclsTpe = internal.refinedType(intersection, originalRefinement.typeSymbol.owner, internal.newScopeWith(strongDecls.toSeq: _*))
val resolvedTags = weakDecls.map {
symbol =>
symbol.name.decodedName.toString -> summonLightTypeTagOfAppropriateKind(symbol.info)
}.toMap
val resolvedTagsExpr = c.Expr[Map[String, LightTypeTag]](Liftable.liftMap[String, Expr[LightTypeTag]].apply(resolvedTags))
(ltagMacro.makeParsedLightTypeTagImpl(strongDeclsTpe), resolvedTagsExpr)
}
// we need to handle three cases – type args, refined types and type bounds (we don't handle type bounds currently)
@inline
protected[this] def mkTagWithTypeParameters[T](tpe: Type, tag: c.WeakTypeTag[T]): c.Expr[Tag[T]] = {
val constructorTag: c.Expr[HKTag[_]] = {
val ctor = tpe.typeConstructor
getCtorKindInfoIfCtorIsTypeParameter(ctor) match {
// type constructor of this type is not a type parameter
// AND not an intersection type
// some of its arguments are type parameters that we should resolve
case None =>
logger.log(s"type A $ctor ${ctor.typeSymbol}")
makeHKTagFromStrongTpe[Any](ctor)
// error: the entire type is just a proper type parameter with no type arguments
case Some(k) if k.params.isEmpty =>
logger.log(s"type B $ctor ${ctor.typeSymbol}")
val msg = s" could not find implicit value for ${tagFormat(tpe)}: $tpe is a type parameter without an implicit Tag!"
addImplicitError(msg)
abortWithImplicitError()
// type constructor is a type parameter AND has type arguments
// we should resolve type constructor separately from an HKTag
case Some(hktKind) =>
logger.log(s"type C $ctor ${ctor.typeSymbol}")
summonHKTag(ctor, hktKind)
}
}
val argTags = {
val args = tpe match {
// preserve wildcards in type arguments
case e: ExistentialTypeApi =>
val exts = e.quantified.toSet
e.underlying.typeArgs.map {
t =>
if (exts.contains(t.typeSymbol)) {
/// generate top-level existential type for LightTypeTagImpl macro
t.typeSymbol.typeSignature match {
case tb: TypeBounds =>
val lo = tb.lo
val hi = tb.hi
val loTag = summonLightTypeTagOfAppropriateKind(lo)
val hiTag = summonLightTypeTagOfAppropriateKind(hi)
reify {
LightTypeTag.wildcardType(loTag.splice, hiTag.splice)
}
case _ =>
summonLightTypeTagOfAppropriateKind(c.internal.existentialType(List(t.typeSymbol), t))
}
} else {
summonLightTypeTagOfAppropriateKind(ReflectionUtil.norm(c.universe: c.universe.type, logger)(t.dealias))
}
}
case _ =>
tpe.typeArgs.map(t => summonLightTypeTagOfAppropriateKind(ReflectionUtil.norm(c.universe: c.universe.type, logger)(t.dealias)))
}
logger.log(s"Now summoning tags for args=$args")
c.Expr[List[LightTypeTag]](Liftable.liftList[c.Expr[LightTypeTag]].apply(args))
}
{
// compiler always inserts WeakTypeTag, by passing it explicitly we slightly reduce fragility
implicit val itag: c.WeakTypeTag[T] = tag
reify {
Tag.appliedTag[T](constructorTag.splice, argTags.splice)
}
}
}
@inline
private[this] final def closestClass(properTypeStrongCtor: Type): c.Expr[Class[_]] = {
// unfortunately .erasure returns trash for intersection types
val tpeLub = ReflectionUtil.norm(c.universe: c.universe.type, logger)(properTypeStrongCtor.dealias) match {
case r: RefinedTypeApi => lub(r.parents)
case o => o
}
val tpeErased = tpeLub.erasure
// and for Scala varargs (Scala by names and Java varargs are fine)
val tpeFixed = if (tpeErased.typeSymbol eq definitions.RepeatedParamClass) {
typeOf[scala.Seq[Any]].dealias
} else if (tpeErased.typeSymbol eq definitions.ArrayClass) {
// workaround for a crash that happens when .erasure misfires on Array in case `Tag[Array[List[X]]]`
// and produces a tree `classOf[Array[List]]` which fails to compile.
// Array is the only type that needs to be parameterized after erasure and stripping its parameters via .erasure
// actually breaks it, so we skip this step for Arrays.
tpeLub.dealias
} else {
tpeErased
}
c.Expr[Class[_]](q"${Literal(Constant(tpeFixed))}.asInstanceOf[_root_.java.lang.Class[_]]")
}
@inline
private[this] final def getCtorKindInfoIfCtorIsTypeParameter(tpe: Type): Option[KindInfo[c.universe.type]] = {
if (!ReflectionUtil.isSelfStrong(Set.empty, tpe)) Some(KindInfo.ofType(tpe))
else None
}
protected[this] def mkTypeParameter(owner: Symbol, kindInfo: KindInfo[c.universe.type]): Symbol = {
import internal.reificationSupport._
import internal.{polyType, typeBounds}
val sym = newNestedSymbol(owner, freshTypeName(""), NoPosition, Flag.PARAM | Flag.DEFERRED, isClass = false)
val origInner = kindInfo.params.map(_.symbol)
def mkBounds(tb: Option[TypeBoundsApi], subst: List[Symbol] = Nil) = tb match {
case Some(b) =>
val (lo, hi) = (b.lo, b.hi)
if (subst.nonEmpty) {
val newLo = lo.substituteSymbols(origInner, subst)
val newHi = hi.substituteSymbols(origInner, subst)
typeBounds(newLo, newHi)
} else typeBounds(lo, hi)
case None => typeBounds(definitions.NothingTpe, definitions.AnyTpe)
}
val tpe = if (kindInfo.params.nonEmpty) {
val params = kindInfo.params.map(mkTypeParameter(sym, _))
polyType(params, mkBounds(kindInfo.bounds, params))
} else mkBounds(kindInfo.bounds)
setInfo(sym, tpe)
sym
}
@inline
protected[this] def mkHKTagArgStruct(tpe: Type, kindInfo: KindInfo[c.universe.type]): Type = {
import internal.reificationSupport._
val staticOwner = c.prefix.tree.symbol.owner
logger.log(s"staticOwner: $staticOwner")
val parents = List(definitions.AnyRefTpe)
val mutRefinementSymbol: Symbol = newNestedSymbol(staticOwner, TypeName(""), NoPosition, FlagsRepr(0L), isClass = true)
val mutArg: Symbol = newNestedSymbol(mutRefinementSymbol, TypeName("Arg"), NoPosition, FlagsRepr(0L), isClass = false)
val isTypeParameter = !ReflectionUtil.isSelfStrong(Set.empty, tpe)
val polyType = if (isTypeParameter && tpe.takesTypeArgs) {
logger.log(s"mkHKTagArgStruct: using etaExpand for type parameter $tpe")
tpe.etaExpand
} else {
val params = kindInfo.params.map(mkTypeParameter(mutArg, _))
mkPolyType(tpe, params)
}
setInfo(mutArg, polyType)
val scope = newScopeWith(mutArg)
setInfo[Symbol](mutRefinementSymbol, RefinedType(parents, scope, mutRefinementSymbol))
RefinedType(parents, scope, mutRefinementSymbol)
}
@inline
protected[this] def mkPolyType(tpe: Type, params: List[c.Symbol]): Type = {
val rhsParams = params.map(symbol => internal.typeRef(NoPrefix, symbol, Nil))
internal.polyType(params, appliedType(tpe, rhsParams))
}
private[this] def summonLightTypeTagOfAppropriateKind(tpe: Type): c.Expr[LightTypeTag] = {
lttFromTag(summonTagForKind(tpe, KindInfo.ofType(tpe)))
}
private[this] def summonHKTag(tpe: Type, kindInfo: KindInfo[c.universe.type]): c.Expr[HKTag[_]] = {
c.Expr[HKTag[_]](summonTagForKind(tpe, kindInfo))
}
@inline
protected[this] def summonTagForKind(tpe: c.Type, kindInfo: KindInfo[c.universe.type]): c.Tree = {
try {
if (kindInfo.params.isEmpty) {
c.inferImplicitValue(appliedType(weakTypeOf[Tag[Nothing]].typeConstructor, tpe), silent = false)
} else {
val ArgStruct = mkHKTagArgStruct(tpe, kindInfo)
logger.log(s"Created implicit Arg: $ArgStruct")
c.inferImplicitValue(appliedType(weakTypeOf[HKTag[Nothing]].typeConstructor, ArgStruct), silent = false)
}
} catch {
case _: TypecheckException =>
val error = hktagSummonHelpfulErrorMessage(tpe, TagMacro.kindOf(kindInfo))
val msg = s" could not find implicit value for ${tagFormat(tpe)}$error"
addImplicitError(msg)
abortWithImplicitError()
}
}
@inline
private[this] final def lttFromTag(tagTree: Tree): c.Expr[LightTypeTag] = {
c.Expr[LightTypeTag](q"$tagTree.tag")
}
def getImplicitError(): String = {
val annotations = symbolOf[Tag[Any]].annotations
annotations
.headOption.flatMap(
AnnotationTools.findArgument(_) {
case Literal(Constant(s: String)) => s
}
).getOrElse(defaultTagImplicitError)
}
def abortWithImplicitError(): Nothing = {
c.abort(c.enclosingPosition, getImplicitError())
}
@inline
protected[this] def addImplicitError(err: String): Unit = {
setImplicitError(s"${getImplicitError()}\n$err")
}
@inline
protected[this] def resetImplicitError(tpe: Type): Unit = {
setImplicitError(defaultTagImplicitError.replace("${T}", tpe.toString))
}
@inline
protected[this] def setImplicitError(err: String): Unit = {
import internal.decorators._
symbolOf[Tag[Any]].setAnnotations(
Annotation(typeOf[implicitNotFound], List[Tree](Literal(Constant(err))), ListMap.empty)
)
()
}
@inline
private[this] final def hktagSummonHelpfulErrorMessage(tpe: Type, kind: Kind): String = {
tagFormatMap.get(kind) match {
case Some(_) => ""
case None =>
val (typaramsWithKinds, appliedParams) = kind
.params.zipWithIndex.map {
case (k, i) =>
val name = s"T${i + 1}"
k.format(name) -> name
}.unzip
s"""
|$tpe is of a kind $kind, which doesn't have a tag name. Please create a tag synonym as follows:
|
| type TagXYZ[${kind.format(typeName = "K")}] = HKTag[ { type Arg[${typaramsWithKinds.mkString(", ")}] = K[${appliedParams.mkString(", ")}] } ]
|
|And use it in your context bound, as in def x[$tpe: TagXYZ] = ...
|OR use Tag.auto.T macro, as in def x[$tpe: Tag.auto.T] = ...""".stripMargin
}
}
}
private object TagMacro {
final val defaultTagImplicitError =
"could not find implicit value for izumi.reflect.Tag[${T}]. Did you forget to put on a Tag, TagK or TagKK context bound on one of the parameters in ${T}? e.g. def x[T: Tag, F[_]: TagK] = ..."
def kindOf(tpe: Universe#Type): Kind = {
Kind(tpe.typeParams.map(t => kindOf(t.typeSignature)))
}
def kindOf(kindInfo: KindInfo[Universe]): Kind = {
Kind(kindInfo.params.map(kindOf))
}
final case class Kind(params: List[Kind]) {
def format(typeName: String) = s"$typeName${if (params.nonEmpty) params.mkString("[", ", ", "]") else ""}"
override def toString: String = format("_")
}
object Kind {
val proper: Kind = Kind(Nil)
}
final def tagFormatMap: Map[Kind, String] = {
Map(
Kind.proper -> "Tag",
Kind(Kind.proper :: Nil) -> "TagK",
Kind(Kind.proper :: Kind.proper :: Nil) -> "TagKK",
Kind(Kind.proper :: Kind.proper :: Kind.proper :: Nil) -> "TagK3",
Kind(Kind(Kind.proper :: Nil) :: Nil) -> "TagT",
Kind(Kind(Kind.proper :: Nil) :: Kind.proper :: Nil) -> "TagTK",
Kind(Kind(Kind.proper :: Nil) :: Kind.proper :: Kind.proper :: Nil) -> "TagTKK",
Kind(Kind(Kind.proper :: Nil) :: Kind.proper :: Kind.proper :: Kind.proper :: Nil) -> "TagTK3"
)
}
final def tagFormat(tpe: Universe#Type): String = {
val kind = kindOf(tpe)
tagFormatMap.get(kind) match {
case Some(t) => s"$t[$tpe]"
case _ => s"HKTag for $tpe of kind $kind"
}
}
}
@NowarnCompat.nowarn("msg=deprecated")
class TagLambdaMacro(override val c: whitebox.Context) extends TagMacro(c) {
import c.universe._
import c.universe.internal.decorators._
def lambdaImpl: c.Tree = {
val pos = c.macroApplication.pos
val targetTpe = c
.enclosingUnit.body.collect {
case AppliedTypeTree(t, arg :: _) if t.exists(_.pos == pos) =>
c.typecheck(
tree = arg,
mode = c.TYPEmode,
pt = c.universe.definitions.NothingTpe,
silent = false,
withImplicitViewsDisabled = true,
withMacrosDisabled = true
).tpe
}.headOption match {
case None =>
c.abort(
c.enclosingPosition,
"Couldn't find the tree of the type that `Tag.auto.T` macro was applied to, please make sure you use the correct syntax, as in `def tagk[F[_]: Tag.auto.T]: TagK[T] = implicitly[Tag.auto.T[F]]`"
)
case Some(t) =>
t
}
val kind = KindInfo.ofType[c.universe.type](targetTpe)
logger.log(s"Found position $pos, target type $targetTpe, target kind $kind")
val ctorParam = mkTypeParameter(NoSymbol, kind)
val ArgStruct = mkHKTagArgStruct(ctorParam.asType.toType, kind)
val resultType = c
.typecheck(
tq"{ type T[${c.internal.typeDef(ctorParam)}] = _root_.izumi.reflect.HKTag[$ArgStruct] }",
c.TYPEmode,
c.universe.definitions.NothingTpe,
silent = false,
withImplicitViewsDisabled = true,
withMacrosDisabled = true
).tpe
val res = Literal(Constant(())).setType(resultType)
logger.log(s"final result: $resultType")
res
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/Tags.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect
import izumi.reflect.macrortti.{LTag, LightTypeTag}
import scala.annotation.implicitNotFound
import scala.language.experimental.macros
trait AnyTag extends Serializable {
def tag: LightTypeTag
/**
* Closest class found for the type or for a LUB of all intersection
* members in case of an intersection type.
*
* A Scala type may not have an associated JVM class, as such
* this class may not be sufficient to create instances of `T`
*
* Only if `tag.hasPreciseClass` returns true
* it may be safe to reflect on `closestClass`
*/
def closestClass: Class[_]
final def hasPreciseClass: Boolean = {
try tag.shortName == closestClass.getSimpleName
catch {
case i: InternalError if i.getMessage == "Malformed class name" => false
}
}
final def =:=(that: AnyTag): Boolean = {
tag =:= that.tag
}
final def <:<(that: AnyTag): Boolean = {
tag <:< that.tag
}
override final def equals(that: Any): Boolean = that match {
case that: AnyTag => this.tag == that.tag
case _ => false
}
override final def hashCode(): Int = tag.hashCode()
override def toString: String = s"AnyTag[$tag]"
}
/**
* Like [[scala.reflect.api.TypeTags.TypeTag]], but supports higher-kinded type tags via `TagK` type class.
*
* In context of DI this lets you define modules parameterized by higher-kinded type parameters.
* This is especially helpful for applying [[https://www.beyondthelines.net/programming/introduction-to-tagless-final/ `tagless final` style]]
*
* Example:
* {{{
* import distage.ModuleDef
*
* class MyModule[F[_]: Monad: TagK] extends ModuleDef {
* make[MyService[F]]
* make[F[Int]].named("lucky-number").from(Monad[F].pure(7))
* }
* }}}
*
* Without a `TagK` constraint above, this example would fail with `no TypeTag available for MyService[F]` error
*
* Currently some limitations apply as to when a `Tag` will be correctly constructed:
* * Type Parameters do not yet resolve in structural refinement methods, e.g. T in {{{ Tag[{ def x: T}] }}}
* They do resolve in refinement type members however, e.g. {{{ Tag[ Any { type Out = T } ] }}}
* * TagK* does not resolve for constructors with bounded parameters, e.g. S in {{{ class Abc[S <: String]; TagK[Abc] }}}
* (You can still have a bound in partial application: e.g. {{{ class Abc[S <: String, A]; TagK[Abc["hi", _]] }}}
* * Further details at [[https://github.com/7mind/izumi/issues/374]]
*
* @see "Lightweight Scala Reflection and why Dotty needs TypeTags reimplemented" https://blog.7mind.io/lightweight-reflection.html
*
* @see [[izumi.reflect.macrortti.LTag]] - summoner for [[izumi.reflect.macrortti.LightTypeTag]] that does not resolve type parameters
* @see [[izumi.reflect.macrortti.LTag.Weak]] - summoner for [[izumi.reflect.macrortti.LightTypeTag]] that does not resolve type parameters and allows unresolved ("weak") type parameters to be part of a tag
*/
@implicitNotFound(
"could not find implicit value for izumi.reflect.Tag[${T}]. Did you forget to put on a Tag, TagK or TagKK context bound on one of the parameters in ${T}? e.g. def x[T: Tag, F[_]: TagK] = ..."
)
trait Tag[T] extends AnyTag {
def tag: LightTypeTag
def closestClass: Class[_]
override final def toString: String = s"Tag[$tag]"
}
object Tag {
/**
* Use `Tag.auto.T[TYPE_PARAM]` syntax to summon a `Tag` for a type parameter of any kind:
*
* {{{
* def module1[F[_]: Tag.auto.T] = new ModuleDef {
* ...
* }
*
* def module2[F[_, _]: Tag.auto.T] = new ModuleDef {
* ...
* }
* }}}
*
* {{{
* def y[K[_[_, _], _[_], _[_[_], _, _, _]](implicit ev: Tag.auto.T[K]): Tag.auto.T[K] = ev
* }}}
*
* {{{
* def x[K[_[_, _], _[_], _[_[_], _, _, _]: Tag.auto.T]: Tag.auto.T[K] = implicitly[Tag.auto.T[K]]
* }}}
*/
def auto: Any = macro TagLambdaMacro.lambdaImpl
@inline def apply[T: Tag]: Tag[T] = implicitly
def apply[T](cls: Class[_], tag0: LightTypeTag): Tag[T] = {
new Tag[T] {
override val tag: LightTypeTag = tag0
override val closestClass: Class[_] = cls
}
}
/**
* Create a Tag of a type formed by applying the type in `tag` to `args`.
*
* Example:
* {{{
* implicit def tagFromTagTAKA[T[_, _[_], _]: TagK3, K[_]: TagK, A0: Tag, A1: Tag]: Tag[T[A0, K, A1]] =
* Tag.appliedTag(TagK3[T].tag, List(Tag[A0].tag, TagK[K].tag, Tag[A1].tag))
* }}}
*/
def appliedTag[R](tag: HKTag[_], args: List[LightTypeTag]): Tag[R] = {
Tag(tag.closestClass, tag.tag.combine(args: _*))
}
/**
* Create a Tag of a type formed from an `intersection` of types (A with B) with a structural refinement taken from `structType`
*
* `structType` is assumed to be a weak type of the entire type, e.g.
* {{{
* Tag[A with B {def abc: Unit}] == refinedTag(classOf[Any], List(LTag[A].tag, LTag[B].tag), LTag.Weak[A with B { def abc: Unit }].tag, Map.empty)
* }}}
*/
def refinedTag[R](lubClass: Class[_], intersection: List[LightTypeTag], structType: LightTypeTag, additionalTypeMembers: Map[String, LightTypeTag]): Tag[R] = {
Tag(lubClass, LightTypeTag.refinedType(intersection, structType, additionalTypeMembers))
}
@deprecated("Binary compatibility for 1.0.0-M6+", "1.0.0-M6")
private[Tag] def refinedTag[R](lubClass: Class[_], intersection: List[LightTypeTag], structType: LightTypeTag): Tag[R] = {
refinedTag(lubClass, intersection, structType, Map.empty)
}
implicit final def tagFromTagMacro[T]: Tag[T] = macro TagMacro.makeTag[T]
}
/**
* Internal unsafe API representing a poly-kinded, higher-kinded type tag.
*
* To create a Tag* implicit for an arbitrary kind use the following syntax:
*
* {{{
* type TagK5[K[_, _, _, _, _]] = HKTag[ { type Arg[A, B, C, D, E] = K[A, B, C, D, E] } ]
* }}}
*
* As an argument to HKTag, you should specify the type variables your type parameter will take and apply them to it, in order.
*
* {{{
* type TagFGC[K[_[_, _], _[_], _[_[_], _, _, _]] = HKTag[ { type Arg[A[_, _], B[_], C[_[_], _, _, _]] = K[A, B, C] } ]
* }}}
*
* A convenience macro `Tag.auto.T` is available to automatically create a type lambda for a type of any kind:
*
* {{{
* def x[K[_[_, _], _[_], _[_[_], _, _, _]: Tag.auto.T]: Tag.auto.T[K] = implicitly[Tag.auto.T[K]]
* }}}
*/
trait HKTag[T] extends AnyTag {
/** Internal `LightTypeTag` holding the `typeConstructor` of type `T` */
def tag: LightTypeTag
def closestClass: Class[_]
override final def toString: String = s"HKTag[$tag]"
}
object HKTag {
def apply[T](cls: Class[_], lightTypeTag: LightTypeTag): HKTag[T] = new HKTag[T] {
override val tag: LightTypeTag = lightTypeTag
override val closestClass: Class[_] = cls
}
def appliedTagNonPos[R](tag: HKTag[_], args: List[Option[LightTypeTag]]): HKTag[R] = {
HKTag(tag.closestClass, tag.tag.combineNonPos(args: _*))
}
def appliedTagNonPosAux[R](cls: Class[_], ctor: LightTypeTag, args: List[Option[LightTypeTag]]): HKTag[R] = {
HKTag(cls, ctor.combineNonPos(args: _*))
}
@inline implicit final def hktagFromTagMacro[T](implicit materializer: HKTagMaterializer[T]): HKTag[T] = materializer.value
}
/**
* Force eager expansion for all recursive implicit searches inside TagMacro
* by introducing a proxy implicit to display better error messages
*
* @see test ResourceEffectBindingsTest."Display tag macro stack trace when ResourceTag is not found"
*/
final class HKTagMaterializer[T](val value: HKTag[T]) extends AnyVal
object HKTagMaterializer {
// FIXME: TagK construction macro
implicit def materializeHKTag[T]: HKTagMaterializer[T] = macro TagMacro.makeHKTagMaterializer[T]
}
// Workaround needed specifically to support generic methods in factories, see `GenericAssistedFactory` and related tests
//
// We need to construct a SafeType signature for a generic method, but generic parameters have no type tags
// So we resort to weak type parameters and pointer equality
trait WeakTag[T] extends AnyTag {
def tag: LightTypeTag
def closestClass: Class[_]
override final def toString: String = s"WeakTag[$tag]"
}
object WeakTag extends WeakTagInstances1 {
def apply[T: WeakTag]: WeakTag[T] = implicitly
def apply[T](cls: Class[_], l: LightTypeTag): WeakTag[T] = {
new WeakTag[T] {
override val tag: LightTypeTag = l
override val closestClass: Class[_] = cls
}
}
implicit def weakTagFromTag[T: Tag]: WeakTag[T] = WeakTag(Tag[T].closestClass, Tag[T].tag)
}
private[reflect] trait WeakTagInstances1 {
implicit def weakTagFromWeakTypeTag[T](implicit l: LTag.Weak[T]): WeakTag[T] = WeakTag(classOf[Any], l.tag)
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/TrivialMacroLogger.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect
import izumi.reflect.internal.fundamentals.platform.console.TrivialLogger
import izumi.reflect.internal.fundamentals.platform.console.TrivialLogger.Config
import scala.reflect.ClassTag
import scala.reflect.macros.blackbox
/**
* To see macro debug output during compilation, set `-Dizumi.reflect.debug.macro.rtti=true` java property! e.g.
*
* {{{
* sbt -Dizumi.reflect.debug.macro.rtti=true compile
* }}}
*
* @see [[DebugProperties]]
*/
private[reflect] object TrivialMacroLogger {
def make[T: ClassTag](c: blackbox.Context): TrivialLogger = {
TrivialLogger.make[T](config = Config(sink = new ScalacSink(c), forceLog = false))
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/macrortti/LTag.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.macrortti
import scala.language.experimental.macros
final case class LTag[T](tag: LightTypeTag)
/**
* these are different summoners for light tags, it's fine for them to be the same structurally
*/
object LTag {
def apply[T: LTag]: LTag[T] = implicitly
implicit def materialize[T]: LTag[T] = macro LightTypeTagMacro.makeStrongTag[T]
final case class Weak[T](tag: LightTypeTag)
object Weak {
def apply[T: LTag.Weak]: LTag.Weak[T] = implicitly
implicit def materialize[T]: LTag.Weak[T] = macro LightTypeTagMacro.makeWeakTag[T]
}
final case class StrongHK[T](tag: LightTypeTag)
object StrongHK {
implicit def materialize[T]: LTag.StrongHK[T] = macro LightTypeTagMacro.makeStrongHKTag[T]
}
final case class WeakHK[T](tag: LightTypeTag)
object WeakHK {
implicit def materialize[T]: LTag.WeakHK[T] = macro LightTypeTagMacro.makeWeakHKTag[T]
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/macrortti/LightTypeTagImpl.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.macrortti
import izumi.reflect.internal.{CollectionCompat, NowarnCompat}
import izumi.reflect.internal.fundamentals.collections.IzCollections._
import izumi.reflect.internal.fundamentals.platform.assertions.IzAssert
import izumi.reflect.internal.fundamentals.platform.console.TrivialLogger
import izumi.reflect.internal.fundamentals.platform.console.TrivialLogger.Config
import izumi.reflect.internal.fundamentals.platform.strings.IzString._
import izumi.reflect.macrortti.LightTypeTagImpl.{Broken, globalCache}
import izumi.reflect.macrortti.LightTypeTagRef.SymName.{SymLiteral, SymTermName, SymTypeName}
import izumi.reflect.macrortti.LightTypeTagRef._
import izumi.reflect.{DebugProperties, ReflectionUtil}
import scala.annotation.tailrec
import scala.collection.mutable
import scala.language.reflectiveCalls
import scala.reflect.api.Universe
object LightTypeTagImpl {
private lazy val globalCache = new java.util.WeakHashMap[Any, AbstractReference]
/** caching is enabled by default for runtime light type tag creation */
private[this] lazy val runtimeCacheEnabled: Boolean = {
System
.getProperty(DebugProperties.`izumi.reflect.rtti.cache.runtime`).asBoolean()
.getOrElse(true)
}
/** Create a LightTypeTag at runtime for a reflected type */
def makeLightTypeTag(u: Universe)(typeTag: u.Type): LightTypeTag = {
ReflectionLock.synchronized {
val logger = TrivialLogger.make[this.type](config = Config.console)
new LightTypeTagImpl[u.type](u, withCache = runtimeCacheEnabled, logger).makeFullTagImpl(typeTag)
}
}
private[this] object ReflectionLock
private sealed trait Broken[T, S] {
def intersectionComponents: Set[T]
def decls: Set[S]
def maybeUnbrokenType: Option[T]
}
private object Broken {
final case class Single[T, S](t: T) extends Broken[T, S] {
override def intersectionComponents: Set[T] = Set(t)
override def decls: Set[S] = Set.empty
override def maybeUnbrokenType: Option[T] = Some(t)
}
final case class Compound[T, S](intersectionComponents: Set[T], decls: Set[S]) extends Broken[T, S] {
override def maybeUnbrokenType: Option[T] = None
}
}
}
final class LightTypeTagImpl[U <: Universe with Singleton](val u: U, withCache: Boolean, logger: TrivialLogger) {
import u._
@inline private[this] final val any = definitions.AnyTpe
@inline private[this] final val obj = definitions.ObjectTpe
@inline private[this] final val nothing = definitions.NothingTpe
@inline private[this] final val ignored = Set(any, obj, nothing)
def makeFullTagImpl(tpe0: Type): LightTypeTag = {
val tpe = Dealias.fullNormDealias(tpe0)
logger.log(s"Initial mainTpe=$tpe:${tpe.getClass} beforeDealias=$tpe0:${tpe0.getClass}")
val lttRef = makeRef(tpe)
val allReferenceComponents = mutable
.LinkedHashSet
.newBuilder[Type]
.++= {
allTypeReferencesWithBases(tpe, mutable.HashSet.empty, onlyIndirect = false)
}.result()
val fullDb = makeFullDb(tpe, allReferenceComponents).toMultimap
val unappliedDb = makeClassOnlyInheritanceDb(tpe, allReferenceComponents.iterator)
LightTypeTag(lttRef, fullDb, unappliedDb)
}
private[this] def allTypeReferencesWithBases(tpe0: Type, basesTermination: mutable.HashSet[Symbol], onlyIndirect: Boolean): Iterator[Type] = {
val allReferenceComponents = allTypeReferences(tpe0, basesTermination, onlyIndirect)
allReferenceComponents.iterator.flatMap {
component =>
if (component.typeSymbol != NoSymbol) {
basesTermination += component.typeSymbol
}
Iterator.single(component) ++ tpeBases(component).flatMap {
case t if basesTermination(t.typeSymbol) => Nil
case t =>
allTypeReferencesWithBases(t, basesTermination, onlyIndirect = true)
}
}
}
// FIXME `allTypeReferences` & `makeRef` should be merged together,
// since they both pass over all visible components of a type in a similar way
private[this] def allTypeReferences(mainTpe: Type, basesTermination: mutable.HashSet[Symbol], onlyIndirect: Boolean): collection.Set[Type] = {
@NowarnCompat.nowarn("msg=deprecated")
@inline def result(): collection.Set[Type] = {
val tpeDealiased = dealiasPrepare(mainTpe).maybeUnbrokenType.getOrElse(NoType)
val inh = mutable.LinkedHashSet.empty[Type]
extractComponents(mainTpe, inh)
logger.log(s"Extracted all type references for mainTpe=$mainTpe parts=${inh.iterator.map(t => (t, t.getClass.asInstanceOf[Class[Any]])).toMap.niceList()}")
if (onlyIndirect) {
inh.retain {
t => t != mainTpe && t != tpeDealiased && !basesTermination(t.typeSymbol)
}
}
inh
}
@inline def dealiasPrepare(t0: Type): Broken[Type, Symbol] = {
UniRefinement.breakRefinement(t0, squashHKTRefToPolyTypeResultType = false)
}
def extractComponents(tpeRaw0: Type, inh: mutable.LinkedHashSet[Type]): Unit = {
val breakResult = dealiasPrepare(tpeRaw0)
val current = breakResult.maybeUnbrokenType
inh ++= current
val intersectionExpansionsArgsBoundsIter: Iterator[Type] = breakResult.intersectionComponents.iterator.flatMap(collectArgsAndBounds)
val refinementDeclMembersIter: Iterator[Type] = breakResult.decls.iterator.flatMap {
sym =>
if (sym.isMethod) {
val m = sym.asMethod
m.returnType :: m.paramLists.iterator.flatten.map(UniRefinement.typeOfParam).toList
} else if (sym.isType) {
UniRefinement.concreteTypesOfTypeMemberOnly(sym)
} else Nil
}
val indirectComponents = intersectionExpansionsArgsBoundsIter ++ refinementDeclMembersIter
indirectComponents.foreach(t => if (!current.contains(t) && !inh(t) && !ignored(t)) extractComponents(t, inh))
}
def collectArgsAndBounds(tpeUnexpanded0: Type): Iterator[Type] = {
// unexpanded: Either
val tpeUnexpanded = Dealias.fullNormDealias(tpeUnexpanded0)
// polyType: [L,R]Either[L,R]
// polyTypeResult: Either[L,R] where L,R are trash symbols
// we need to use tpe.etaExpand but 2.13 has a bug: https://github.com/scala/bug/issues/11673#
// tpe.etaExpand.resultType.dealias.typeArgs.flatMap(_.dealias.resultType.typeSymbol.typeSignature match {
def doExtractNonParamTypeArgs(t: Type): List[Type] = {
val tpePolyTypeResultType = Dealias.fullNormDealiasSquashHKTToPolyTypeResultType(t)
logger.log(
s"""Got tpeUnexpanded=$tpeUnexpanded:${tpeUnexpanded.getClass} args=${tpeUnexpanded.typeArgs} params=${tpeUnexpanded.typeParams}
|tpePolyTypeResultType=$tpePolyTypeResultType:${tpePolyTypeResultType.getClass} args=${tpePolyTypeResultType.typeArgs} params=${tpePolyTypeResultType.typeParams}""".stripMargin
)
tpePolyTypeResultType.typeArgs.flatMap {
targ0 =>
val targ = Dealias.fullNormDealias(targ0)
val targSym = targ.typeSymbol
targSym.typeSignature match {
case t: TypeBoundsApi =>
Seq(t.hi, t.lo).filterNot(ignored)
case _ =>
if (!targSym.isParameter) {
Seq(targ0)
} else {
Seq.empty
}
}
}
}
val tparamTypeBoundsAndTypeArgs = tpeUnexpanded match {
case e: ExistentialTypeApi =>
doExtractNonParamTypeArgs(e.underlying)
case o =>
doExtractNonParamTypeArgs(o)
}
logger.log(s"tparamTypeBoundsAndTypeArgs of $tpeUnexpanded=$tparamTypeBoundsAndTypeArgs")
/**
* Don't do this:
* Iterator.single(tpePolyTypeResultType) -- produces trash symbols out of skolems
* tpePolyTypeResultType.typeArgs.iterator -- just redundant, included in `tparamTypeBoundsAndTypeArgs`
*/
Iterator.single(tpeUnexpanded) ++
tpeUnexpanded.typeArgs.iterator ++
tparamTypeBoundsAndTypeArgs.iterator
}
result()
}
private[this] def makeFullDb(tpe: Type, allReferenceComponents: Iterable[Type]): Iterator[(AbstractReference, AbstractReference)] = {
val stableBases = makeAppliedBases(tpe, allReferenceComponents.iterator)
val basesAsLambdas = makeLambdaOnlyBases(tpe, allReferenceComponents.iterator)
basesAsLambdas.iterator ++ stableBases.iterator
}
private[this] def makeAppliedBases(mainTpe: Type, allReferenceComponents: Iterator[Type]): List[(AbstractReference, AbstractReference)] = {
val appliedBases = allReferenceComponents
.filterNot(isHKTOrPolyTypeOrResultTypeArtifact) // remove PolyTypes, only process applied types in this inspection
.filterNot(isExistentialArtifact) // remove forSome artifacts in Scala 2.11 && 2.12
.flatMap {
component =>
val tparams = component.etaExpand.typeParams
val lambdaParams = makeLambdaParams(None, tparams).toMap
val appliedParents = tpeBases(component).filterNot(isHKTOrPolyTypeOrResultTypeArtifact)
val componentRef = makeRef(component)
appliedParents.map {
parentTpe =>
val parentRef = makeRefTop(parentTpe, terminalNames = lambdaParams, isLambdaOutput = lambdaParams.nonEmpty) match {
case unapplied: Lambda =>
if (unapplied.someArgumentsReferenced) {
unapplied
} else {
logger.log(
s"No arguments referenced in l=$unapplied, parentTpe=$parentTpe(etaExpand:${parentTpe.etaExpand}), tparams=$tparams, mainTpe=$mainTpe(etaExpand:${mainTpe.etaExpand})"
)
unapplied.output
}
case applied: AppliedReference =>
applied
}
(componentRef, parentRef)
}
}
.filterNot {
case (t, parent) =>
// IzAssert(parent != t, parent -> t) // 2.11/2.12 fail this
parent == t
}
.toList
logger.log(s"Computed applied bases for tpe=$mainTpe appliedBases=${appliedBases.toMultimap.niceList()}")
appliedBases
}
private[this] def makeLambdaOnlyBases(mainTpe: Type, allReferenceComponents: Iterator[Type]): List[(AbstractReference, AbstractReference)] = {
@inline def result(): List[(AbstractReference, AbstractReference)] = {
val unappliedBases = allReferenceComponents.flatMap(processLambdasReturningRefinements).toList
logger.log(s"Computed lambda only bases for tpe=$mainTpe lambdaBases=${unappliedBases.toMultimap.niceList()}")
unappliedBases
}
def processLambdasReturningRefinements(tpeRaw0: Type): List[(AbstractReference, AbstractReference)] = {
val componentsOfPolyTypeResultType = UniRefinement.breakRefinement(tpeRaw0, squashHKTRefToPolyTypeResultType = true)
IzAssert(
assertion = {
if (componentsOfPolyTypeResultType.maybeUnbrokenType.isEmpty) {
!componentsOfPolyTypeResultType.intersectionComponents.exists(_.takesTypeArgs)
} else {
true
}
},
clue = {
s"""Unexpected intersection contains a PolyType:
|tpeRaw0 = $tpeRaw0
|components = ${componentsOfPolyTypeResultType.intersectionComponents.niceList(prefix = "*")}
|takesTypeArgs = ${componentsOfPolyTypeResultType.intersectionComponents.map(_.takesTypeArgs).niceList(prefix = "*")}
|etaExpand = ${componentsOfPolyTypeResultType.intersectionComponents.map(_.etaExpand).niceList(prefix = "+")}
|tparams = ${componentsOfPolyTypeResultType.intersectionComponents.map(_.etaExpand.typeParams).niceList(prefix = "-")}
|""".stripMargin
}
)
componentsOfPolyTypeResultType
.intersectionComponents.iterator.flatMap {
component =>
val componentAsPolyType = component.etaExpand
val tparams = componentAsPolyType.typeParams
if (tparams.isEmpty) {
Nil
} else {
makeLambda(componentAsPolyType, tparams)
}
}.toList
}
def makeLambda(componentAsPolyType: Type, tparams: List[Symbol]): List[(AbstractReference, AbstractReference)] = {
val lambdaParams = makeLambdaParams(None, tparams)
val parentLambdas = makeLambdaParents(componentAsPolyType, lambdaParams)
val componentLambda = makeRef(componentAsPolyType) // : LightTypeTagRef.Lambda
IzAssert(componentLambda.isInstanceOf[Lambda])
parentLambdas.map(componentLambda -> _)
}
def makeLambdaParents(componentPolyType: Type, lambdaParams: List[(String, SymName.LambdaParamName)]): List[AbstractReference] = {
val allBaseTypes = tpeBases(componentPolyType)
val paramMap = lambdaParams.toMap
lazy val lambdaParamsUnpacked = lambdaParams.map(_._2)
allBaseTypes.map {
parentTpe =>
val reference = makeRefTop(parentTpe, terminalNames = paramMap, isLambdaOutput = false)
reference match {
case l: Lambda =>
l
case applied: AppliedReference =>
val l = Lambda(lambdaParamsUnpacked, applied)
// Some(l).filter(_.allArgumentsReferenced) // do not include non-lambda parents such as Product into lambda's inheritance tree
// No, include ALL bases for lambdas (this should be more correct since lambda is a template for a full parameterized db after combine)
if (l.someArgumentsReferenced) l else applied
}
}
}
result()
}
private[this] def makeClassOnlyInheritanceDb(mainTpe: Type, allReferenceComponents: Iterator[Type]): Map[NameReference, Set[NameReference]] = {
val baseclassReferences = allReferenceComponents
.flatMap {
// squash all type lambdas and get the intersection of their results
// because we don't care about type parameters at all in this inspection
UniRefinement.breakRefinement(_, squashHKTRefToPolyTypeResultType = true).intersectionComponents
}
.flatMap {
component =>
val prefix = makePrefixReference(component)
val componentRef = makeNameReference(component, component.typeSymbol, Boundaries.Empty, prefix)
val appliedBases = tpeBases(component).filterNot(isHKTOrPolyType)
appliedBases.map(componentRef -> makeRef(_))
}
val unparameterizedInheritanceData = baseclassReferences
.toMultimap
.map {
case (t, parents) =>
t -> parents
.collect {
case r: AppliedNamedReference =>
r.asName
}
.filterNot {
parent =>
// IzAssert(parent != t, parent -> t) // 2.11/2.12 fail this
parent == t
}
}
.filterNot(_._2.isEmpty)
logger.log(s"Computed unparameterized inheritance data for tpe=$mainTpe unappliedBases=${unparameterizedInheritanceData.toMultimap.niceList()}")
unparameterizedInheritanceData
}
private[this] def tpeBases(t0: Type): List[Type] = {
// val tpef = Dealias.fullNormDealiasResultType(t0, squashHKTRefToPolyTypeResultType = false)
// no PolyTypes passed to here [but actually we should preserve polyTypes]
val tpe = Dealias.fullNormDealias(t0)
val upperBound = {
val tpeSig = tpe.typeSymbol.typeSignature
tpeSig.finalResultType match {
// handle abstract higher-kinded type members specially,
// move their upper bound into inheritance db, because they
// will lose it after application. (Unlike proper type members)
case b: TypeBoundsApi if tpeSig.takesTypeArgs =>
List(b.hi)
case _ =>
Nil
}
}
(upperBound.iterator ++ tpe
.baseClasses
.iterator
.map(tpe.baseType))
.filterNot(ignored)
.filterNot(if (isSingletonType(tpe)) _ => false else _.typeSymbol.fullName == tpe.typeSymbol.fullName)
.filterNot(_ =:= tpe) // 2.11/2.12 fail this
.toList
}
private[this] def makeRef(tpe: Type): AbstractReference = {
if (withCache) {
globalCache.synchronized(globalCache.get(tpe)) match {
case null =>
val ref = makeRefTop(tpe, terminalNames = Map.empty, isLambdaOutput = false)
globalCache.synchronized(globalCache.put(tpe, ref))
ref
case ref =>
ref
}
} else {
makeRefTop(tpe, terminalNames = Map.empty, isLambdaOutput = false)
}
}
private[this] def makeRefTop(tpe: Type, terminalNames: Map[String, SymName.LambdaParamName], isLambdaOutput: Boolean): AbstractReference = {
this.makeRefImpl(0, nestedIn = Set(tpe), terminalNames, Set.empty)(tpe, isLambdaOutput)
}
private[this] def makeRefImpl(
level: Int,
nestedIn: Set[Type],
terminalNames: Map[String, SymName.LambdaParamName],
knownWildcards: Set[Symbol]
)(tpe0: Type,
isLambdaOutput: Boolean
): AbstractReference = {
def makeBoundaries(t: Type): Boundaries = {
val tOrTypeSymBounds = t match {
case b: TypeBoundsApi => b
case _ => t.typeSymbol.typeSignature
}
tOrTypeSymBounds match {
case b: TypeBoundsApi =>
if ((b.lo =:= nothing && b.hi =:= any) ||
// prevent recursion
(nestedIn.contains(b.lo) || nestedIn.contains(b.hi))) {
Boundaries.Empty
} else {
val lo = makeRefSub(b.lo, Map.empty, Set.empty)
val hi = makeRefSub(b.hi, Map.empty, Set.empty)
Boundaries.Defined(lo, hi)
}
case _ =>
Boundaries.Empty
}
}
def makeRefSub(tpe: Type, stop: Map[String, SymName.LambdaParamName], knownWildcardsSub: Set[Symbol]): AbstractReference = {
val allWildcards = knownWildcards ++ knownWildcardsSub
if (allWildcards.contains(tpe.typeSymbol)) {
WildcardReference(makeBoundaries(tpe0))
} else {
this.makeRefImpl(level + 1, nestedIn + tpe, terminalNames ++ stop, allWildcards)(tpe, isLambdaOutput = false)
}
}
val thisLevel = logger.sub(level)
def unpackLambda(t: TypeApi): AbstractReference = {
val polyType = t.etaExpand
val polyTypeResult = Dealias.fullNormDealiasSquashHKTToPolyTypeResultType(polyType)
val tparams = polyType.typeParams
val nestingLevel = if (level > 0) Some(level) else None
val lambdaParams = makeLambdaParams(nestingLevel, tparams)
thisLevel.log(s"✴️ λ type $t has parameters $lambdaParams and result $polyTypeResult terminal names = $terminalNames")
val reference = makeRefSub(polyTypeResult, lambdaParams.toMap, Set.empty)
val out = Lambda(lambdaParams.map(_._2), reference)
if (!out.allArgumentsReferenced) {
val kvParams = lambdaParams.map { case (k, v) => s"$v = $k" }
thisLevel.log(
s"⚠️ unused 𝝺 args! type $t => $out, someReferenced: ${out.someArgumentsReferenced} context: $terminalNames, 𝝺 params: $kvParams, 𝝺 result: $polyTypeResult => $reference, referenced: ${out.referenced} "
)
}
thisLevel.log(s"✳️ Restored λ $t => ${out.longNameWithPrefix}")
out
}
def unpackProperTypeRefinement(t0: Type, rules: Map[String, SymName.LambdaParamName]): AppliedReference = {
IzAssert(!isHKTOrPolyType(Dealias.fullNormDealias(t0)))
UniRefinement.breakRefinement(t0, squashHKTRefToPolyTypeResultType = false) match {
case Broken.Compound(components, decls) =>
val parts = components.map(unpackAsProperType(_, rules): AppliedReference)
val intersection = LightTypeTagRef.maybeIntersection(parts)
if (decls.nonEmpty) {
Refinement(intersection, decls.flatMap(convertDecl(_, rules)))
} else {
intersection
}
case Broken.Single(t) =>
unpackAsProperType(t, rules)
}
}
def unpackAsProperType(tpeRaw: Type, rules: Map[String, SymName.LambdaParamName]): AppliedNamedReference = {
val tpe = Dealias.fullNormDealias(tpeRaw)
val prefix = makePrefixReference(tpe)
val typeSymbol = tpe.typeSymbol
val boundaries = makeBoundaries(tpe)
val nameRef = rules.get(typeSymbol.fullName) match {
case Some(lambdaParameter) =>
// this is a previously encountered type variable
NameReference(lambdaParameter, boundaries, prefix)
case None =>
makeNameReference(tpe, typeSymbol, boundaries, prefix)
}
tpe.typeArgs match {
case Nil =>
nameRef
case args =>
val tparams = Dealias.fullNormDealias(tpeRaw).typeConstructor.typeParams
val refParams = tpeRaw match {
case t: ExistentialTypeApi =>
val quantifiedParams = t.quantified.toSet
t.underlying.typeArgs.zip(tparams).map {
case (arg, param) =>
val paramRef =
if (quantifiedParams.contains(arg.typeSymbol) && !rules.contains(arg.typeSymbol.fullName)) {
WildcardReference(makeBoundaries(arg))
} else {
makeRefSub(arg, Map.empty, quantifiedParams)
}
TypeParam(paramRef, makeVariance(param.asType))
}
case _ =>
args.zip(tparams).map {
case (arg, param) =>
val paramRef = makeRefSub(arg, Map.empty, Set.empty)
TypeParam(paramRef, makeVariance(param.asType))
}
}
val res = FullReference(nameRef.asName.ref, refParams, prefix)
thisLevel.log(s"Assembled FullReference=$res from args=$args and tparams=$tparams")
res
}
}
def convertDecl(decl: Symbol, rules: Map[String, SymName.LambdaParamName]): CollectionCompat.IterableOnce[RefinementDecl] = {
if (decl.isMethod) {
val declMethod = decl.asMethod
val returnTpe = declMethod.returnType
val paramLists0 = declMethod
.paramLists.map(_.map {
param =>
val paramTpe = UniRefinement.typeOfParam(param)
makeRefSub(paramTpe, rules, Set.empty).asInstanceOf[AppliedReference]
})
val paramLists = if (paramLists0.nonEmpty) paramLists0 else List(Nil)
paramLists.map {
parameterList =>
RefinementDecl.Signature(declMethod.name.decodedName.toString, parameterList, makeRefSub(returnTpe, rules, Set.empty).asInstanceOf[AppliedReference])
}
} else if (decl.isType) {
val tpe = UniRefinement.typeOfTypeMember(decl)
val declName = decl.name.decodedName.toString
val ref = makeRefSub(tpe, rules, Set.empty) match {
// inspecting abstract type will always return a NamedReference
case n @ NameReference(SymTypeName(""), _, _) => n.copy(ref = SymTypeName(declName))
case ref => ref
}
Some(RefinementDecl.TypeMember(declName, ref))
} else {
None
}
}
val out = tpe0 match {
case l if isLambdaOutput => // this is required for handling SwapF2, etc.
IzAssert(!isHKTOrPolyType(l), l -> l.getClass)
val out = Lambda(terminalNames.values.toList, unpackAsProperType(l, terminalNames))
out
case l: PolyTypeApi =>
val out = unpackLambda(l)
out
case l if l.takesTypeArgs =>
if (terminalNames.contains(l.typeSymbol.fullName)) {
val out = unpackAsProperType(l, terminalNames)
out
} else {
val out = unpackLambda(l)
out
}
// special case top-level existential type (may be generated by TagMacro)
case e: ExistentialType if e.quantified.headOption.contains(e.underlying.typeSymbol) =>
WildcardReference(makeBoundaries(e.underlying))
case c =>
unpackProperTypeRefinement(c, terminalNames)
}
out
}
private[this] def makeLambdaParams(ctxIdx: Option[Int], tparams: List[Symbol]): List[(String, SymName.LambdaParamName)] = {
tparams.zipWithIndex.map {
case (tparamSym, idx) =>
val fullName = tparamSym.fullName
// val idxStr = ctxIdx match {
// case Some(ctx) =>
// s"$ctx:$idx"
// case None =>
// idx.toString
// }
fullName -> SymName.LambdaParamName(idx, ctxIdx.getOrElse(LightTypeTagRef.LambdaConstants.defaultContextId), tparams.size)
}
}
private[this] def makeNameReference(originalType: Type, typeSymbol: Symbol, boundaries: Boundaries, prefix: Option[AppliedReference]): NameReference = {
originalType match {
case c: ConstantTypeApi =>
NameReference(SymLiteral(c.value.value), boundaries, None)
case s: SingleTypeApi if s.sym != NoSymbol =>
s.sym.info.finalResultType match { // finalResultType necessary to dereference NullaryMethodType (from vals in 2.13.10+)
case c: ConstantTypeApi =>
NameReference(SymLiteral(c.value.value), boundaries, None)
case _ =>
val (sym, _) = Dealias.dealiasSingletons(s.termSymbol, originalType)
val resultType = Dealias.fullNormDealias(sym.typeSignatureIn(s.pre).finalResultType)
val newPrefix = if (hasSingletonType(resultType.typeSymbol)) makePrefixReference(resultType) else prefix
NameReference(makeSymName(sym), boundaries, newPrefix)
}
case _ =>
NameReference(makeSymName(typeSymbol), boundaries, prefix)
}
}
private[this] def makePrefixReference(originalType: Type): Option[AppliedReference] = {
@tailrec def extractPrefix(t0: Type): Option[Type] = {
t0 match {
case t: TypeRefApi => Some(t.pre).filterNot(_ == NoPrefix)
case t: SingleTypeApi => Some(t.pre).filterNot(_ == NoPrefix)
case t: ExistentialTypeApi => extractPrefix(t.underlying)
case t: ThisTypeApi => extractPrefix(if (t.sym.isType) t.sym.asType.toType else t.sym.asTerm.typeSignature)
case _ => None
}
}
def unpackPrefix(pre: Type): Option[AppliedReference] = {
pre match {
case i if i.typeSymbol.isPackage =>
None
case k if k == NoPrefix =>
None
case k: ThisTypeApi =>
k.sym.asType.toType match {
// This case matches UniRefinement.unapply#it.RefinementTypeRef case
case UniRefinement(_, _) =>
None
case _ =>
if (originalType.termSymbol != NoSymbol) {
fromRef(originalType.termSymbol.owner.asType.toType)
} else {
fromRef(originalType.typeSymbol.owner.asType.toType)
}
}
case k if k.termSymbol != NoSymbol =>
handleSingletonType(k)
case o =>
fromRef(o)
}
}
def handleSingletonType(initType: Type): Some[NameReference] = {
def tryDealiasSingletons(tpe: Type): (Symbol, Type) = {
// Using `termSymbol` to convert from a type to a `Symbol` and then back to a type
// can be potentially lossy.
// This is relevant in cases where the type is a singleton for an effectively
// final definition, in which case the symbol we will get is dealiased to the symbol
// from the original base class:
// ```
// trait Base { object Inner }
// object Sub extends Base
// ```
// Calling `termSymbol` on `Sub.Inner.type` will produce `Base.Inner` since
// `Inner` is effectively final and `Sub.Inner` can be viewed as an alias for `Base.Inner`.
// When we go back to a type to continue building the prefix we will lose the original
// type information we started with.
// We want to avoid that and maintain the correct `Sub` prefix.
// As of writing the `dealiasSingletons` logic below is relevant only
// for `val` aliases. In other cases we want to avoid the lossy conversion.
// Here we check whether the symbol stands for an `object` (module) if so, we skip
// the dealiasing logic for singletons to avoid the potential loss of type information.
val termSym = tpe.termSymbol
val shouldDealias = !termSym.isModule
if (shouldDealias) {
Dealias.dealiasSingletons(termSym, tpe)
} else {
(termSym, tpe)
}
}
val (dealiasedSym, rawTpe0) = tryDealiasSingletons(initType)
val dealiasedTpe = Dealias.fullNormDealias(rawTpe0)
val name = makeSymName(dealiasedSym)
val prePrefix = makePrefixReference(dealiasedTpe)
Some(NameReference(name, Boundaries.Empty, prePrefix))
}
def fromRef(o: Type): Option[AppliedReference] = {
makeRef(o) match {
case a: AppliedReference =>
Some(a)
case o =>
throw new IllegalStateException(s"Cannot extract prefix from $originalType: expected applied reference, but got $o")
}
}
val prefix = extractPrefix(originalType)
val unpacked = prefix.flatMap(unpackPrefix)
unpacked
}
private[this] def makeSymName(sym: Symbol): SymName = {
val o = sym.owner
val base = if (o.asInstanceOf[{ def hasMeaninglessName: Boolean }].hasMeaninglessName) {
sym.name.decodedName.toString
} else {
sym.fullName
}
if (hasSingletonType(sym)) {
SymTermName(base)
} else {
SymTypeName(base)
}
}
private[this] def makeVariance(tpes: TypeSymbol): Variance = {
if (tpes.isCovariant) {
Variance.Covariant
} else if (tpes.isContravariant) {
Variance.Contravariant
} else {
Variance.Invariant
}
}
private[this] object UniRefinement {
def unapply(tpe: Type): Option[(List[Type], List[Symbol])] = {
(tpe: AnyRef) match {
case x: scala.reflect.internal.Types#RefinementTypeRef =>
Some((x.parents.asInstanceOf[List[Type]], x.decls.toList.asInstanceOf[List[Symbol]]))
case r: RefinedTypeApi @unchecked =>
Some((r.parents, r.decls.toList))
case _ =>
None
}
}
def breakRefinement(t0: Type, squashHKTRefToPolyTypeResultType: Boolean): Broken[Type, Symbol] = {
breakRefinement0(t0, squashHKTRefToPolyTypeResultType) match {
case (t, d) if d.isEmpty && t.size == 1 =>
Broken.Single(t.head)
case (t, d) =>
logger.log(s"Found compound type parents=$t decls=$d")
Broken.Compound(t, d)
}
}
private[this] def breakRefinement0(t0: Type, squashHKTRefToPolyTypeResultType: Boolean): (Set[Type], Set[Symbol]) = {
val normalized = if (squashHKTRefToPolyTypeResultType) {
Dealias.fullNormDealiasSquashHKTToPolyTypeResultType(t0)
} else {
Dealias.fullNormDealias(t0)
}
normalized match {
case UniRefinement(parents, decls) =>
val parts = parents.map(breakRefinement0(_, squashHKTRefToPolyTypeResultType))
val types = parts.flatMap(_._1)
val partsDecls = parts.flatMap(_._2)
(types.toSet, (decls ++ partsDecls).toSet)
case t =>
(Set(t), Set.empty)
}
}
def typeOfParam(p: Symbol): Type = {
p.typeSignature
}
def typeOfTypeMember(decl: Symbol): Type = {
if (decl.isAbstract) {
// Generate a type like {type F = F|<λ %2:0 → Nothing..λ %2:0 → Any>}
// on Scala 2 for abstract type lambdas like `type F[A] <: Any`
// This form is inferior to Scala 3's default {type F = F|}
// But it's valid for comparisons
val invertTypeBoundsForPolyTypes = {
decl.typeSignature match {
case _: PolyTypeApi =>
internal.typeBounds(
lo = decl.typeSignature.map { case TypeBounds(lo, _) => lo; case t => t },
hi = decl.typeSignature.map { case TypeBounds(_, hi) => hi; case t => t }
)
case _ =>
decl.typeSignature
}
}
invertTypeBoundsForPolyTypes
} else {
decl.typeSignature
}
}
def concreteTypesOfTypeMemberOnly(decl: Symbol): List[Type] = {
if (decl.isAbstract) {
decl.typeSignature match {
case TypeBounds(lo, hi) => List(lo, hi)
case _ => Nil
// we ignore higher kinded type members like `type F[A] = A`
// because supporting them is highly non-straightforward (unlike on Scala 3)
}
} else {
List(decl.typeSignature)
}
}
}
private[this] object Dealias {
def fullNormDealiasSquashHKTToPolyTypeResultType(t0: Type): Type = {
var prev = null: Type
val t1 = fullNormDealias(t0)
var cur = if (t1.takesTypeArgs) t1.etaExpand else t1
while (cur ne prev) {
prev = cur
cur = norm(prev).dealias.resultType
}
cur
}
def fullNormDealias(t0: Type): Type = {
scala211ExistentialDealiasWorkaround(t0)
}
// On Scala 2.12+ .dealias automatically destroys wildcards by using TypeMaps#ExistentialExtrapolation on dealiased existential output
// This is kinda bad. But whatever, we're stuck with this behavior for this moment, so we should emulate it on 2.11 to make it work too.
@tailrec
private[this] def scala211ExistentialDealiasWorkaround(t0: Type): Type = {
t0 match {
case existential: ExistentialTypeApi =>
internal.existentialType(existential.quantified, scala211ExistentialDealiasWorkaroundLoop(existential.underlying.dealias))
// internal.existentialAbstraction(existential.quantified, existential.underlying.dealias)
case t =>
val next = norm(t).dealias
if (next eq t) {
t
} else {
scala211ExistentialDealiasWorkaround(next)
}
}
}
private[this] def scala211ExistentialDealiasWorkaroundLoop(t0: Type): Type = scala211ExistentialDealiasWorkaround(t0)
@tailrec
def dealiasSingletons(termSymbol: Symbol, termSymbolTpe: Type): (Symbol, Type) = {
val resultTermTpe = termSymbol.typeSignature.finalResultType
val resultTerm = resultTermTpe.termSymbol
if (hasSingletonType(resultTerm)) {
dealiasSingletons(resultTerm, resultTermTpe)
} else {
(termSymbol, termSymbolTpe)
}
}
@inline def norm(x: Type): Type = {
ReflectionUtil.norm(u: u.type, logger)(x)
}
}
private[this] def isHKTOrPolyTypeOrResultTypeArtifact(tpe: Type): Boolean = {
isHKTOrPolyType(tpe) || (!ReflectionUtil.isAbstractType(tpe) && tpe.typeArgs.exists {
targ =>
val tSym = targ.typeSymbol
targ.isInstanceOf[Universe#TypeRefApi] && tSym.isParameter && tSym.isType && !ReflectionUtil.isIdentityLikeTypeLambda(targ)
})
}
private[this] def isHKTOrPolyType(tpe: Type): Boolean = {
tpe.takesTypeArgs || tpe.isInstanceOf[PolyTypeApi]
}
private[this] def isExistentialArtifact(tpe: Type): Boolean = {
tpe.typeArgs.exists {
targ =>
val tSym = targ.typeSymbol
tSym.isType && tSym.asType.isExistential
}
}
private[this] def hasSingletonType(sym: Symbol): Boolean = {
sym.isTerm || sym.isModuleClass || isSingletonType(sym.typeSignature)
}
@inline private[this] def isSingletonType(tpe: Type): Boolean = {
tpe.isInstanceOf[SingletonTypeApi] && !tpe.isInstanceOf[ThisTypeApi]
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/macrortti/LightTypeTagMacro.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.macrortti
import izumi.reflect.internal.fundamentals.platform.console.TrivialLogger
import izumi.reflect.{DebugProperties, ReflectionUtil, TrivialMacroLogger}
import scala.reflect.macros.blackbox
final class LightTypeTagMacro(override val c: blackbox.Context)
extends LightTypeTagMacro0[blackbox.Context](c)(
logger = TrivialMacroLogger.make[LightTypeTagMacro](c)
)
private[reflect] class LightTypeTagMacro0[C <: blackbox.Context](val c: C)(logger: TrivialLogger) {
import c.universe._
protected final def cacheEnabled: Boolean = !c.settings.contains(s"${DebugProperties.`izumi.reflect.rtti.cache.compile`}=false")
protected final val impl = new LightTypeTagImpl[c.universe.type](c.universe, withCache = cacheEnabled, logger)
final def makeStrongHKTag[ArgStruct: c.WeakTypeTag]: c.Expr[LTag.StrongHK[ArgStruct]] = {
val tpe = unpackArgStruct(weakTypeOf[ArgStruct])
if (ReflectionUtil.allPartsStrong(tpe)) {
c.Expr[LTag.StrongHK[ArgStruct]](q"new ${weakTypeOf[LTag.StrongHK[ArgStruct]]}(${makeParsedLightTypeTagImpl(tpe)})")
} else {
c.abort(c.enclosingPosition, s"Can't materialize LTag.StrongHKTag[$tpe]: found unresolved type parameters in $tpe")
}
}
final def makeWeakHKTag[ArgStruct: c.WeakTypeTag]: c.Expr[LTag.WeakHK[ArgStruct]] = {
c.Expr[LTag.WeakHK[ArgStruct]](q"new ${weakTypeOf[LTag.WeakHK[ArgStruct]]}(${makeParsedLightTypeTagImpl(unpackArgStruct(weakTypeOf[ArgStruct]))})")
}
final def makeStrongTag[T: c.WeakTypeTag]: c.Expr[LTag[T]] = {
val tpe = weakTypeOf[T]
if (ReflectionUtil.allPartsStrong(tpe.dealias)) {
val res = makeParsedLightTypeTagImpl(tpe)
c.Expr[LTag[T]](q"new ${weakTypeOf[LTag[T]]}($res)")
} else {
c.abort(c.enclosingPosition, s"Can't materialize LTag[$tpe]: found unresolved type parameters in $tpe")
}
}
final def makeWeakTag[T: c.WeakTypeTag]: c.Expr[LTag.Weak[T]] = {
val res = makeParsedLightTypeTagImpl(weakTypeOf[T])
c.Expr[LTag.Weak[T]](q"new ${weakTypeOf[LTag.Weak[T]]}($res)")
}
final def makeParsedLightTypeTag[T: c.WeakTypeTag]: c.Expr[LightTypeTag] = {
makeParsedLightTypeTagImpl(weakTypeOf[T])
}
final def makeParsedLightTypeTagImpl(tpe: Type): c.Expr[LightTypeTag] = {
val res = impl.makeFullTagImpl(tpe)
makeParsedLightTypeTagImpl(res)
}
final def makeParsedLightTypeTagImpl(ltt: LightTypeTag): c.Expr[LightTypeTag] = {
logger.log(s"LightTypeTagImpl: created LightTypeTag: $ltt")
val serialized = ltt.serialize()
val hashCodeRef = serialized.hash
val strRef = serialized.ref
val strDBs = serialized.databases
c.Expr[LightTypeTag](
q"_root_.izumi.reflect.macrortti.LightTypeTag.parse($hashCodeRef: _root_.scala.Int, $strRef : _root_.java.lang.String, $strDBs : _root_.java.lang.String, ${LightTypeTag.currentBinaryFormatVersion}: _root_.scala.Int)"
)
}
@inline final def unpackArgStruct(t: Type): Type = {
def badShapeError() = {
c.abort(
c.enclosingPosition,
s"Expected type shape RefinedType `{ type Arg[A] = X[A] }` for summoning `LTag.StrongHK/WeakHK[X]`, but got $t (raw: ${showRaw(t)} ${t.getClass})"
)
}
t match {
case r: RefinedTypeApi =>
r.decl(TypeName("Arg")) match {
case sym: TypeSymbolApi =>
sym.info.typeConstructor
case _ => badShapeError()
}
case _ => badShapeError()
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/macrortti/package.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect
import scala.language.experimental.macros
package object macrortti {
type LWeakTag[T] = LTag.Weak[T]
object LWeakTag {
def apply[T: LWeakTag]: LWeakTag[T] = implicitly
}
type LTagK[K[_]] = LTag.StrongHK[{ type Arg[A] = K[A] }]
type LTagKK[K[_, _]] = LTag.StrongHK[{ type Arg[A, B] = K[A, B] }]
type LTagK3[K[_, _, _]] = LTag.StrongHK[{ type Arg[A, B, C] = K[A, B, C] }]
type LTagT[K[_[_]]] = LTag.StrongHK[{ type Arg[A[_]] = K[A] }]
type LTagTK[K[_[_], _]] = LTag.StrongHK[{ type Arg[A[_], B] = K[A, B] }]
type LTagTKK[K[_[_], _, _]] = LTag.StrongHK[{ type Arg[A[_], B, C] = K[A, B, C] }]
type LTagTK3[K[_[_], _, _, _]] = LTag.StrongHK[{ type Arg[A[_], B, C, D] = K[A, B, C, D] }]
object LTagK {
/**
* Construct a type tag for a higher-kinded type `K[_]`
*
* Example:
* {{{
* LTagK[Option]
* }}}
*/
def apply[K[_]: LTagK]: LTagK[K] = implicitly
}
object LTagKK {
def apply[K[_, _]: LTagKK]: LTagKK[K] = implicitly
}
object LTagK3 {
def apply[K[_, _, _]: LTagK3]: LTagK3[K] = implicitly
}
object LTagT {
def apply[K[_[_]]: LTagT]: LTagT[K] = implicitly
}
object LTagTK {
def apply[K[_[_], _]: LTagTK]: LTagTK[K] = implicitly
}
object LTagTKK {
def apply[K[_[_], _, _]: LTagTKK]: LTagTKK[K] = implicitly
}
object LTagTK3 {
def apply[K[_[_], _, _, _]: LTagTK3]: LTagTK3[K] = implicitly
}
// simple materializers
def LTT[T]: LightTypeTag = macro LightTypeTagMacro.makeParsedLightTypeTag[T]
def `LTT[_]`[T[_]]: LightTypeTag = macro LightTypeTagMacro.makeParsedLightTypeTag[T[Nothing]]
def `LTT[+_]`[T[+_]]: LightTypeTag = macro LightTypeTagMacro.makeParsedLightTypeTag[T[Nothing]]
def `LTT[A,B,_>:B<:A]`[A, B <: A, T[_ >: B <: A]]: LightTypeTag = macro LightTypeTagMacro.makeParsedLightTypeTag[T[Nothing]]
def `LTT[A[_],_[_[x] <: A[x]]`[A[_], T[B[x] <: A[x]]]: LightTypeTag = macro LightTypeTagMacro.makeParsedLightTypeTag[T[Nothing]]
def `LTT[_[_]]`[T[_[_]]]: LightTypeTag = macro LightTypeTagMacro.makeParsedLightTypeTag[T[Nothing]]
def `LTT[_[+_]]`[T[_[+_]]]: LightTypeTag = macro LightTypeTagMacro.makeParsedLightTypeTag[T[Nothing]]
def `LTT[_[+_[_]]]`[T[_[+_[_]]]]: LightTypeTag = macro LightTypeTagMacro.makeParsedLightTypeTag[T[Nothing]]
def `LTT[_[_[_]]]`[T[_[_[_]]]]: LightTypeTag = macro LightTypeTagMacro.makeParsedLightTypeTag[T[Nothing]]
def `LTT[_,_]`[T[_, _]]: LightTypeTag = macro LightTypeTagMacro.makeParsedLightTypeTag[T[Nothing, Nothing]]
def `LTT[_[_,_]]`[T[_[_, _]]]: LightTypeTag = macro LightTypeTagMacro.makeParsedLightTypeTag[T[Nothing]]
def `LTT[_[+_,+_]]`[T[_[+_, +_]]]: LightTypeTag = macro LightTypeTagMacro.makeParsedLightTypeTag[T[Nothing]]
def `LTT[_[_,_],_,_]`[T[_[_, _], _, _]]: LightTypeTag = macro LightTypeTagMacro.makeParsedLightTypeTag[T[Nothing, Nothing, Nothing]]
def `LTT[_[+_,+_],_,_]`[T[_[+_, +_], _, _]]: LightTypeTag = macro LightTypeTagMacro.makeParsedLightTypeTag[T[Nothing, Nothing, Nothing]]
def `LTT[_[_],_[_]]`[T[_[_], _[_]]]: LightTypeTag = macro LightTypeTagMacro.makeParsedLightTypeTag[T[Nothing, Nothing]]
def `LTT[_[_[_],_[_]]]`[T[_[_[_], _[_]]]]: LightTypeTag = macro LightTypeTagMacro.makeParsedLightTypeTag[T[Nothing]]
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/package.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi
package object reflect {
/**
* `TagK` is like a [[scala.reflect.api.TypeTags.TypeTag]] but for higher-kinded types.
*
* Example:
* {{{
* def containerTypesEqual[F[_]: TagK, K[_]: TagK]): Boolean = TagK[F].tag.tpe =:= TagK[K].tag.tpe
*
* containerTypesEqual[Set, collection.immutable.Set] == true
* containerTypesEqual[Array, List] == false
* }}}
*/
type TagK[K[_]] = HKTag[{ type Arg[A] = K[A] }]
type TagKK[K[_, _]] = HKTag[{ type Arg[A, B] = K[A, B] }]
type TagK3[K[_, _, _]] = HKTag[{ type Arg[A, B, C] = K[A, B, C] }]
type TagT[K[_[_]]] = HKTag[{ type Arg[A[_]] = K[A] }]
type TagTK[K[_[_], _]] = HKTag[{ type Arg[A[_], B] = K[A, B] }]
type TagTKK[K[_[_], _, _]] = HKTag[{ type Arg[A[_], B, C] = K[A, B, C] }]
type TagTK3[K[_[_], _, _, _]] = HKTag[{ type Arg[A[_], B, C, D] = K[A, B, C, D] }]
object TagK {
/**
* Construct a type tag for a higher-kinded type `K[_]`
*
* Example:
* {{{
* TagK[Option]
* }}}
*/
@inline def apply[K[_]: TagK]: TagK[K] = implicitly
}
object TagKK {
@inline def apply[K[_, _]: TagKK]: TagKK[K] = implicitly
}
object TagK3 {
@inline def apply[K[_, _, _]: TagK3]: TagK3[K] = implicitly
}
object TagT {
@inline def apply[K[_[_]]: TagT]: TagT[K] = implicitly
}
object TagTK {
@inline def apply[K[_[_], _]: TagTK]: TagTK[K] = implicitly
}
object TagTKK {
@inline def apply[K[_[_], _, _]: TagTKK]: TagTKK[K] = implicitly
}
object TagTK3 {
@inline def apply[K[_[_], _, _, _]: TagTK3]: TagTK3[K] = implicitly
}
type TagK4[K[_, _, _, _]] = HKTag[{ type Arg[A0, A1, A2, A3] = K[A0, A1, A2, A3] }]
type TagK5[K[_, _, _, _, _]] = HKTag[{ type Arg[A0, A1, A2, A3, A4] = K[A0, A1, A2, A3, A4] }]
type TagK6[K[_, _, _, _, _, _]] = HKTag[{ type Arg[A0, A1, A2, A3, A4, A5] = K[A0, A1, A2, A3, A4, A5] }]
type TagK7[K[_, _, _, _, _, _, _]] = HKTag[{ type Arg[A0, A1, A2, A3, A4, A5, A6] = K[A0, A1, A2, A3, A4, A5, A6] }]
type TagK8[K[_, _, _, _, _, _, _, _]] = HKTag[{ type Arg[A0, A1, A2, A3, A4, A5, A6, A7] = K[A0, A1, A2, A3, A4, A5, A6, A7] }]
type TagK9[K[_, _, _, _, _, _, _, _, _]] = HKTag[{ type Arg[A0, A1, A2, A3, A4, A5, A6, A7, A8] = K[A0, A1, A2, A3, A4, A5, A6, A7, A8] }]
type TagK10[K[_, _, _, _, _, _, _, _, _, _]] = HKTag[{ type Arg[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9] = K[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9] }]
type TagK11[K[_, _, _, _, _, _, _, _, _, _, _]] = HKTag[{ type Arg[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10] = K[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10] }]
type TagK12[K[_, _, _, _, _, _, _, _, _, _, _, _]] =
HKTag[{ type Arg[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11] = K[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11] }]
type TagK13[K[_, _, _, _, _, _, _, _, _, _, _, _, _]] =
HKTag[{ type Arg[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12] = K[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12] }]
type TagK14[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _]] =
HKTag[{ type Arg[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13] = K[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13] }]
type TagK15[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] =
HKTag[{ type Arg[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14] = K[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14] }]
type TagK16[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] =
HKTag[{ type Arg[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15] = K[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15] }]
type TagK17[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] = HKTag[{
type Arg[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16] = K[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16]
}]
type TagK18[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] = HKTag[{
type Arg[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17] =
K[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17]
}]
type TagK19[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] = HKTag[{
type Arg[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18] =
K[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18]
}]
type TagK20[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] = HKTag[{
type Arg[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19] =
K[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19]
}]
type TagK21[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] = HKTag[{
type Arg[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20] =
K[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20]
}]
type TagK22[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] = HKTag[{
type Arg[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21] =
K[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21]
}]
// TODO
// type TagKUBound[U, K[_ <: U]] = HKTag[{ type Arg[A <: U] = K[A] }]
// object TagKUBound {
// def apply[U, K[_ <: U]](implicit ev: TagKUBound[U, K]): TagKUBound[U, K] = implicitly
// }
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2.11/izumi/reflect/internal/NowarnCompat/nowarn.scala
================================================
package izumi.reflect.internal.NowarnCompat
private[reflect] class nowarn(value: String = "") extends scala.annotation.ClassfileAnnotation
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2.12+/izumi/reflect/internal/NowarnCompat.scala
================================================
package izumi.reflect.internal
private[reflect] object NowarnCompat {
private[reflect] final type nowarn = scala.annotation.nowarn
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2.12-/izumi/reflect/internal/CollectionCompat.scala
================================================
package izumi.reflect.internal
private[reflect] object CollectionCompat {
private[reflect] final type IterableOnce[+A] = scala.collection.TraversableOnce[A]
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2.12-/izumi/reflect/internal/OrderingCompat.scala
================================================
package izumi.reflect.internal
import scala.collection.mutable
private[reflect] object OrderingCompat {
@inline private[reflect] def listOrdering[A](ordering: Ordering[A]): Ordering[List[A]] = {
Ordering.Implicits.seqDerivedOrdering(ordering)
}
@inline private[reflect] def arrayOrdering[A](ordering: Ordering[A]): Ordering[Array[A]] = {
Ordering.Implicits.seqDerivedOrdering[ArraySeqLike, A](ordering).on(array => array)
}
private[reflect] final type ArraySeqLike[A] = mutable.WrappedArray[A]
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2.12-/izumi/reflect/internal/fundamentals/platform/language/unused.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.internal.fundamentals.platform.language
private[reflect] final class unused extends deprecated("unused", "unused")
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2.13+/izumi/reflect/internal/CollectionCompat.scala
================================================
package izumi.reflect.internal
private[reflect] object CollectionCompat {
private[reflect] final type IterableOnce[+A] = scala.collection.IterableOnce[A]
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2.13+/izumi/reflect/internal/OrderingCompat.scala
================================================
package izumi.reflect.internal
import scala.collection.mutable
private[reflect] object OrderingCompat {
@inline private[reflect] def listOrdering[A](ordering: Ordering[A]): Ordering[List[A]] = {
Ordering.Implicits.seqOrdering(ordering)
}
@inline private[reflect] def arrayOrdering[A](ordering: Ordering[A]): Ordering[Array[A]] = {
Ordering.Implicits.seqOrdering[ArraySeqLike, A](ordering).on(array => array)
}
private[reflect] final type ArraySeqLike[A] = mutable.ArraySeq[A]
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-2.13+/izumi/reflect/internal/fundamentals/platform/language/package.scala
================================================
package izumi.reflect.internal.fundamentals.platform
package object language {
private[reflect] type unused = scala.annotation.unused
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/TagMacro.scala
================================================
package izumi.reflect
import scala.quoted.{Expr, Quotes, Type, Varargs}
import izumi.reflect.macrortti.{LightTypeTag, LightTypeTagRef}
import izumi.reflect.dottyreflection.{Inspect, InspectorBase, ReflectionUtil}
import izumi.reflect.macrortti.LightTypeTagRef.{FullReference, NameReference, SymName, TypeParam, Variance}
import scala.collection.mutable
object TagMacro {
def createTagExpr[A <: AnyKind: Type](using Quotes): Expr[Tag[A]] =
new TagMacro().createTagExpr[A]
}
final class TagMacro(using override val qctx: Quotes) extends InspectorBase {
import qctx.reflect._
override def shift: Int = 0
private val tagSymbol = Symbol.requiredClass("izumi.reflect.Tag")
private val tagSymbolTypeRef = tagSymbol.typeRef
private val tagObjSymbol = Symbol.requiredModule("izumi.reflect.Tag")
private val tagObjApplyMethodSym = tagObjSymbol.declaredMethod("apply").find(_.paramSymss(1).size == 2).get
private lazy val tagWildCardTpe = Type.of[Tag[?]]
def createTagExpr[A <: AnyKind: Type]: Expr[Tag[A]] = {
val owners = getClassDefOwners(Symbol.spliceOwner)
val typeRepr = TypeRepr.of[A]._dealiasSimplifiedFull
if (allPartsStrong(owners, typeRepr)) {
createTag[A](typeRepr)
} else {
summonCombinedTag[A](owners, typeRepr)
}
}
private def createTag[A <: AnyKind](typeRepr0: TypeRepr): Expr[Tag[A]] = {
val typeRepr = typeRepr0._etaExpandTypeRef // convert HKT type refs to type lambdas manually as an optimization. This required an additional splice level before.
val ltt = Inspect.inspectTypeRepr(typeRepr)
val cls = closestClassOfTypeRepr(typeRepr)
Apply(
fun = TypeApply(
fun = Select(qualifier = Ref.term(tagObjSymbol.termRef), symbol = tagObjApplyMethodSym),
args = List(Inferred(typeRepr))
),
args = List(cls.asTerm, ltt.asTerm)
).asExpr.asInstanceOf[Expr[Tag[A]]]
}
private def summonCombinedTag[T <: AnyKind: Type](owners: Set[Symbol], typeReprDealiased: TypeRepr): Expr[Tag[T]] = {
def summonLTT(typeRepr: TypeRepr): Expr[LightTypeTag] = {
typeRepr match {
case TypeBounds(low, high) =>
val lowTag = summonTag[T](low)
val highTag = summonTag[T](high)
'{ LightTypeTag.wildcardType($lowTag.tag, $highTag.tag) }
case _ =>
val result = summonTag[T](typeRepr)
'{ $result.tag }
}
}
def summonIfNotLambdaParamOf(typeRepr: TypeRepr, lam: TypeRepr): Expr[Option[LightTypeTag]] = {
if (isLambdaParamOf(typeRepr, lam)) {
'{ None }
} else {
val tag = summonLTT(typeRepr)
'{ Some($tag) }
}
}
def isLambdaParamOf(typeRepr: TypeRepr, lam: TypeRepr): Boolean = {
typeRepr match {
case ref: ParamRef if ref.binder == lam => true
case _ => false
}
}
typeReprDealiased match {
case outerLambda: TypeLambda =>
outerLambda.resType match {
case AppliedType(ctorTpe, typeArgsTpes) =>
val paramsRange = 0 until outerLambda.paramNames.size
val isSimpleApplication = typeArgsTpes.collect {
case ref: ParamRef if ref.binder == outerLambda => ref.paramNum
} == paramsRange
val constructorTag = summonTag[T](ctorTpe)
if (isSimpleApplication) {
val argsTags = Expr.ofList(typeArgsTpes.map(a => summonIfNotLambdaParamOf(a, outerLambda)))
'{ Tag.appliedTagNonPos[T](${ constructorTag }, ${ argsTags }) }
} else {
val distinctNonParamArgsTypes = typeArgsTpes.filter(!isLambdaParamOf(_, outerLambda)).distinct
val outerLambdaParamArgsTypeParamRefs = paramsRange.map(outerLambda.param(_)).toList
val arity = 1 + distinctNonParamArgsTypes.size + outerLambdaParamArgsTypeParamRefs.size
val fullParamTail = (distinctNonParamArgsTypes ++ outerLambdaParamArgsTypeParamRefs)
.iterator.distinct.zipWithIndex
val typeArgToLambdaParameterMap = fullParamTail.map {
case (argTpe, idx) =>
val idxPlusOne = idx + 1
val lambdaParameter = SymName.LambdaParamName(idxPlusOne, LightTypeTagRef.LambdaConstants.tagMacro, arity)
argTpe -> lambdaParameter
}.toMap
val usageOrderDistinctNonLambdaArgs = distinctNonParamArgsTypes.map(t => typeArgToLambdaParameterMap(t))
val declarationOrderLambdaParamArgs = outerLambdaParamArgsTypeParamRefs.map(t => typeArgToLambdaParameterMap(t))
val completeTail = usageOrderDistinctNonLambdaArgs ::: declarationOrderLambdaParamArgs
val usages = typeArgsTpes.map(t => TypeParam(NameReference(typeArgToLambdaParameterMap(t)), Variance.Invariant))
// we give a distinct lambda parameter to the constructor, even if constructor is one of the type parameters
val firstParamIdx = 0
assert(completeTail.size + 1 == arity)
val ctorLambdaParameter = SymName.LambdaParamName(firstParamIdx, LightTypeTagRef.LambdaConstants.tagMacro, arity)
val ctorApplyingLambda =
LightTypeTagRef.Lambda(
ctorLambdaParameter :: completeTail,
FullReference(ctorLambdaParameter, usages)
)
log(s"""HK non-trivial lambda construction:
|ctorApplyingLambda=$ctorApplyingLambda
|usageOrderNonLambdaArgs=$usageOrderDistinctNonLambdaArgs
|declarationOrderLambdaParamArgs=$declarationOrderLambdaParamArgs
|""".stripMargin)
val argTagsExceptCtor = {
val nonParamArgsDealiased = distinctNonParamArgsTypes.map(_._dealiasSimplifiedFull)
log(s"HK COMPLEX Now summoning tags for args=$nonParamArgsDealiased outerLambdaParams=$outerLambdaParamArgsTypeParamRefs")
Expr.ofList(
nonParamArgsDealiased.map(t => '{ Some(${ summonLTT(t) }) }) ++ outerLambdaParamArgsTypeParamRefs.map(_ => '{ None })
)
}
val outerLambdaReprTag = Inspect.makeParsedLightTypeTagImpl(LightTypeTag(ctorApplyingLambda, Map.empty, Map.empty))
'{
val ctorTag = ${ constructorTag }.asInstanceOf[Tag[Any]]
Tag.appliedTagNonPosAux[T](ctorTag.closestClass, ${ outerLambdaReprTag }, Some(ctorTag.tag) :: ${ argTagsExceptCtor })
}
}
case other =>
// TODO add support for and/or/refinement, see test with `IntersectionBlockingIO`
report.warning(
s"""TODO: Pathological intersection refinement result in lambda being reconstructed result=`${other.show}` in the rhs of type lambda lam=`${outerLambda.show}`
|Only simple applied types of form F[A] are supported in results of type lambdas. The generated tag will not work correctly.""".stripMargin
)
createTag[T](outerLambda)
}
case AppliedType(ctor, args) =>
val ctorTag = summonTag[T](ctor)
val argsTags = Expr.ofList(args.map(summonLTT))
'{ Tag.appliedTag[T](${ ctorTag }, ${ argsTags }) }
case andType: AndType =>
val tpes = flattenAnd(andType)
val ltts: Expr[List[LightTypeTag]] = Expr.ofList(tpes.map(summonLTT))
val cls = Literal(ClassOfConstant(lubClassOf(typeReprDealiased, tpes))).asExpr.asInstanceOf[Expr[Class[?]]]
val dummyAnyStructLtt = {
// FIXME add constructor for intersections without the unused on Scala 3 struct type
Inspect.inspectAny[Any]
}
'{ Tag.refinedTag[T](${ cls }, ${ ltts }, ${ dummyAnyStructLtt }, Map.empty) }
case orType: OrType =>
val tpes = flattenOr(orType)
val ltts: Expr[List[LightTypeTag]] = Expr.ofList(tpes.map(summonLTT))
val cls = Literal(ClassOfConstant(lubClassOf(typeReprDealiased, tpes))).asExpr.asInstanceOf[Expr[Class[?]]]
'{ Tag.unionTag[T](${ cls }, ${ ltts }) }
case refinement: Refinement =>
val (members, parent) = flattenRefinements(refinement)
val cls = closestClassOfTypeRepr(parent)
val parentLtt = summonLTT(parent)
val (allTypeMembers, termMembers) = members.partitionMap {
case (s, n, tb: TypeBounds) if allPartsStrong(owners, tb) => Left(Left((n, tb)))
case (_, n, TypeBounds(lo, hi)) if lo == hi => Left(Right((n, hi)))
case (_, _, tb @ TypeBounds(lo, hi)) =>
report.errorAndAbort(
s"TagMacro: resolving type parameters inside type bounds is not supported, got weak types in bounds=${tb.show}, in type=$typeReprDealiased"
)
case x => Right(x)
}
val (strongTypeBounds, weakTypeMembers) = allTypeMembers.partitionMap(identity)
// FIXME: once we add resolution for method/val members too, not just type members
// this struct will no longer be 'weak'. In fact we'll want to add a new constructor
// instead of `refinedTag` that will be better suited to fully resolved struct tags
val termAndStrongTpesOnlyWeakStructLtt = {
val termOnlyRefinementTypeRepr = termMembers.foldRight(defn.AnyRefClass.typeRef: TypeRepr) {
case ((_, name, tpe), refinement) =>
Refinement(parent = refinement, name = name, info = tpe)
}
val withStrongTpesRefinementTypeRepr = strongTypeBounds.foldRight(termOnlyRefinementTypeRepr) {
case ((name, tpe), refinement) =>
Refinement(parent = refinement, name = name, info = tpe)
}
Inspect.inspectTypeRepr(withStrongTpesRefinementTypeRepr)
}
val resolvedTypeMemberLtts = weakTypeMembers.map {
case (name, tpe) => '{ (${ Expr(name) }, ${ summonLTT(tpe) }) }
}
// NB: we're resolving LTTs anew for all type members here, instead of optimizing
// to resolve only for 'weak' members as in Scala 2.
log(
s"""Got refinement $refinement
|parent=$parent
|members=$members
|closestClass=$cls
|""".stripMargin
)
'{ Tag.refinedTag[T](${ cls }, List(${ parentLtt }), ${ termAndStrongTpesOnlyWeakStructLtt }, Map(${ Varargs(resolvedTypeMemberLtts) }: _*)) }
// error: the entire type is just a proper type parameter with no type arguments
// it cannot be resolved further
case x if ReflectionUtil.topLevelWeakType(owners, Set.empty, x) =>
val tStr = x.show
val implicitMessage = defaultImplicitError.replace("${T}", tStr)
report.errorAndAbort(s"""$tStr is a type parameter without an implicit Tag!
| $implicitMessage
|""".stripMargin)
case _ =>
report.errorAndAbort(s"Unsupported type in TagMacro.summonCombinedTag: $typeReprDealiased")
}
}
private def closestClassOfTypeRepr(typeRepr: TypeRepr): Expr[Class[?]] = {
Literal(ClassOfConstant(lubClassOf(typeRepr, intersectionUnionRefinementClassPartsOf(typeRepr)))).asExpr.asInstanceOf[Expr[Class[?]]]
}
private def lubClassOf(specificTpe: TypeRepr, tpes: List[TypeRepr]): TypeRepr = {
tpes.map(_.baseClasses) match {
case h :: t =>
val bases = h.to(mutable.LinkedHashSet)
t.foreach {
b =>
val bBases = b.to(mutable.HashSet)
bases.filterInPlace(bBases)
}
// rely on the fact that .baseClasses returns classes in order from most specific to least, therefore most specific class should be first.
val baseClass = bases.headOption.getOrElse(defn.AnyClass)
// try to recover type parameters of the specific type e.g. to support Arrays
// see https://github.com/zio/izumi-reflect/issues/474 & https://github.com/scala/scala3/issues/21916
specificTpe.baseType(baseClass)
// FIXME: below doesn't work, need to treat AnyVals specially
// bases.find(!_.typeRef.baseClasses.contains(defn.AnyValClass)).getOrElse(defn.AnyClass).typeRef
case Nil =>
defn.AnyClass.typeRef
}
}
private def summonTag[T <: AnyKind](typeRepr: TypeRepr)(using outerCombinedType: Type[T]): Expr[Tag[?]] = {
val tagTypeRepr = AppliedType(tagSymbolTypeRef, List(typeRepr))
Implicits.search(tagTypeRepr) match {
case s: ImplicitSearchSuccess =>
s.tree.asExpr.asInstanceOf[Expr[Tag[?]]]
case f: ImplicitSearchFailure =>
val aStr = typeRepr.show
val implicitMessage = defaultImplicitError.replace("${T}", aStr)
val message = s"""Error when creating a combined tag for ${Type.show[T]}, when summoning Tag for part of that type $aStr:
| $implicitMessage
|Structure of overall type was: `${TypeRepr.of[T]}`
|Structure of part of the type was: `$typeRepr
|Stack trace: ${locally {
import java.io.{PrintWriter, StringWriter}
val t = new Exception()
val sw = new StringWriter()
t.printStackTrace(new PrintWriter(sw))
sw.toString
}}""".stripMargin
report.errorAndAbort(message)
}
}
private inline val defaultImplicitError =
"could not find implicit value for izumi.reflect.Tag[${T}]. Did you forget to put on a Tag, TagK or TagKK context bound on one of the parameters in ${T}? e.g. def x[T: Tag, F[_]: TagK] = ..."
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/Tags.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect
import izumi.reflect.dottyreflection.Inspect
import izumi.reflect.macrortti.{LTag, LightTypeTag, LightTypeTagRef}
import scala.annotation.implicitNotFound
trait AnyTag extends Serializable {
def tag: LightTypeTag
/**
* Closest class found for the type or for a LUB of all intersection
* members in case of an intersection type.
*
* A Scala type may not have an associated JVM class, as such
* this class may not be sufficient to create instances of `T`
*
* Only if `tag.hasPreciseClass` returns true
* it may be safe to reflect on `closestClass`
*/
def closestClass: Class[_]
final def hasPreciseClass: Boolean = {
try tag.shortName == closestClass.getSimpleName
catch {
case i: InternalError if i.getMessage == "Malformed class name" => false
}
}
final def =:=(that: AnyTag): Boolean = {
tag =:= that.tag
}
final def <:<(that: AnyTag): Boolean = {
tag <:< that.tag
}
override final def equals(that: Any): Boolean = that match {
case that: AnyTag => this.tag == that.tag
case _ => false
}
override final def hashCode(): Int = tag.hashCode()
override def toString: String = s"AnyTag[$tag]"
}
/**
* Like [[scala.reflect.api.TypeTags.TypeTag]], but supports higher-kinded type tags via `TagK` type class.
*
* In context of DI this lets you define modules parameterized by higher-kinded type parameters.
* This is especially helpful for applying [[https://www.beyondthelines.net/programming/introduction-to-tagless-final/ `tagless final` style]]
*
* Example:
* {{{
* import distage.ModuleDef
*
* class MyModule[F[_]: Monad: TagK] extends ModuleDef {
* make[MyService[F]]
* make[F[Int]].named("lucky-number").from(Monad[F].pure(7))
* }
* }}}
*
* Without a `TagK` constraint above, this example would fail with `no TypeTag available for MyService[F]` error
*
* Currently some limitations apply as to when a `Tag` will be correctly constructed:
* * Type Parameters do not yet resolve in structural refinement methods, e.g. T in {{{ Tag[{ def x: T}] }}}
* They do resolve in refinement type members however, e.g. {{{ Tag[ Any { type Out = T } ] }}}
* * TagK* does not resolve for constructors with bounded parameters, e.g. S in {{{ class Abc[S <: String]; TagK[Abc] }}}
* (You can still have a bound in partial application: e.g. {{{ class Abc[S <: String, A]; TagK[Abc["hi", _]] }}}
* * Further details at [[https://github.com/7mind/izumi/issues/374]]
*
* @see "Lightweight Scala Reflection and why Dotty needs TypeTags reimplemented" https://blog.7mind.io/lightweight-reflection.html
*
* @see [[izumi.reflect.macrortti.LTag]] - summoner for [[izumi.reflect.macrortti.LightTypeTag]] that does not resolve type parameters
* @see [[izumi.reflect.macrortti.LTag.Weak]] - summoner for [[izumi.reflect.macrortti.LightTypeTag]] that does not resolve type parameters and allows unresolved ("weak") type parameters to be part of a tag
*/
@implicitNotFound(
"could not find implicit value for izumi.reflect.Tag[${T}]. Did you forget to put on a Tag, TagK or TagKK context bound on one of the parameters in ${T}? e.g. def x[T: Tag, F[_]: TagK] = ..."
)
trait Tag[T <: AnyKind] extends AnyTag {
def tag: LightTypeTag
def closestClass: Class[_]
override final def toString: String = s"Tag[$tag]"
}
object Tag {
/**
* Use `Tag.auto.T[TYPE_PARAM]` syntax to summon a `Tag` for a type parameter of any kind:
*
* NOTE: On Scala 3+ it's the same as `Tag[T]`
*/
object auto {
type T[X <: AnyKind] = Tag[X]
}
@inline def apply[T <: AnyKind: Tag]: Tag[T] = implicitly
def apply[T <: AnyKind](cls: Class[_], tag0: LightTypeTag): Tag[T] = {
new Tag[T] {
override val tag: LightTypeTag = tag0
override val closestClass: Class[_] = cls
}
}
/**
* Create a Tag of a type formed by applying the type in `tag` to `args`.
*
* Example:
* {{{
* implicit def tagFromTagTAKA[T[_, _[_], _]: TagK3, K[_]: TagK, A0: Tag, A1: Tag]: Tag[T[A0, K, A1]] =
* Tag.appliedTag(TagK3[T].tag, List(Tag[A0].tag, TagK[K].tag, Tag[A1].tag))
* }}}
*/
def appliedTag[R <: AnyKind](tag: Tag[_], args: List[LightTypeTag]): Tag[R] = {
Tag(tag.closestClass, tag.tag.combine(args: _*))
}
def appliedTagNonPos[R <: AnyKind](tag: Tag[_], args: List[Option[LightTypeTag]]): Tag[R] = {
Tag(tag.closestClass, tag.tag.combineNonPos(args: _*))
}
def appliedTagNonPosAux[R <: AnyKind](cls: Class[_], ctor: LightTypeTag, args: List[Option[LightTypeTag]]): Tag[R] = {
Tag(cls, ctor.combineNonPos(args: _*))
}
def unionTag[R <: AnyKind](lubClass: Class[_], union: List[LightTypeTag]): Tag[R] = {
Tag(lubClass, LightTypeTag.unionType(union))
}
/**
* Create a Tag of a type formed from an `intersection` of types (A with B) with a structural refinement taken from `structType`
*
* `structType` is assumed to be contain the structural refinements of the entire type, e.g.
* {{{
* Tag[A with B {def abc: Unit}] == refinedTag(classOf[Any], List(LTag[A].tag, LTag[B].tag), LTag.Weak[{ def abc: Unit }].tag, Map.empty)
* }}}
*/
def refinedTag[R <: AnyKind](
lubClass: Class[_],
intersection: List[LightTypeTag],
structType: LightTypeTag,
additionalTypeMembers: Map[String, LightTypeTag]
): Tag[R] = {
Tag(lubClass, LightTypeTag.refinedType(intersection, structType, additionalTypeMembers))
}
inline implicit final def tagFromTagMacro[T <: AnyKind]: Tag[T] = ${ TagMacro.createTagExpr[T] }
}
/**
* Internal unsafe API representing a poly-kinded, higher-kinded type tag.
*
* NOTE: On Scala 3+ it's the same as `Tag[T]`
*/
type HKTag[T <: AnyKind] = Tag[T]
object HKTag {
def apply[T](cls: Class[_], lightTypeTag: LightTypeTag): Tag[T] = Tag(cls, lightTypeTag)
def appliedTag[R <: AnyKind](tag: Tag[_], args: List[LightTypeTag]): Tag[R] = Tag.appliedTag(tag, args)
def appliedTagNonPos[R <: AnyKind](tag: Tag[_], args: List[Option[LightTypeTag]]): Tag[R] = Tag.appliedTagNonPos(tag, args)
def appliedTagNonPosAux[R](cls: Class[_], ctor: LightTypeTag, args: List[Option[LightTypeTag]]): Tag[R] = Tag.appliedTagNonPosAux(cls, ctor, args)
}
/**
* `TagK` is like a [[scala.reflect.api.TypeTags.TypeTag]] but for higher-kinded types.
*
* NOTE: On Scala 3+ it's the same as `Tag[T]`
*/
type TagK[K[_]] = Tag[K]
type TagKK[K[_, _]] = Tag[K]
type TagK3[K[_, _, _]] = Tag[K]
type TagT[K[_[_]]] = Tag[K]
type TagTK[K[_[_], _]] = Tag[K]
type TagTKK[K[_[_], _, _]] = Tag[K]
type TagTK3[K[_[_], _, _, _]] = Tag[K]
object TagK {
/**
* Construct a type tag for a higher-kinded type `K[_]`
*
* Example:
* {{{
* TagK[Option]
* }}}
*/
@inline def apply[K[_]: TagK]: TagK[K] = implicitly
}
object TagKK {
@inline def apply[K[_, _]: TagKK]: TagKK[K] = implicitly
}
object TagK3 {
@inline def apply[K[_, _, _]: TagK3]: TagK3[K] = implicitly
}
object TagT {
@inline def apply[K[_[_]]: TagT]: TagT[K] = implicitly
}
object TagTK {
@inline def apply[K[_[_], _]: TagTK]: TagTK[K] = implicitly
}
object TagTKK {
@inline def apply[K[_[_], _, _]: TagTKK]: TagTKK[K] = implicitly
}
object TagTK3 {
@inline def apply[K[_[_], _, _, _]: TagTK3]: TagTK3[K] = implicitly
}
type TagK4[K[_, _, _, _]] = Tag[K]
type TagK5[K[_, _, _, _, _]] = Tag[K]
type TagK6[K[_, _, _, _, _, _]] = Tag[K]
type TagK7[K[_, _, _, _, _, _, _]] = Tag[K]
type TagK8[K[_, _, _, _, _, _, _, _]] = Tag[K]
type TagK9[K[_, _, _, _, _, _, _, _, _]] = Tag[K]
type TagK10[K[_, _, _, _, _, _, _, _, _, _]] = Tag[K]
type TagK11[K[_, _, _, _, _, _, _, _, _, _, _]] = Tag[K]
type TagK12[K[_, _, _, _, _, _, _, _, _, _, _, _]] = Tag[K]
type TagK13[K[_, _, _, _, _, _, _, _, _, _, _, _, _]] = Tag[K]
type TagK14[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _]] = Tag[K]
type TagK15[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] = Tag[K]
type TagK16[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] = Tag[K]
type TagK17[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] = Tag[K]
type TagK18[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] = Tag[K]
type TagK19[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] = Tag[K]
type TagK20[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] = Tag[K]
type TagK21[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] = Tag[K]
type TagK22[K[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]] = Tag[K]
// Workaround needed specifically to support generic methods in factories, see `GenericAssistedFactory` and related tests
//
// We need to construct a SafeType signature for a generic method, but generic parameters have no type tags
// So we resort to weak type parameters and pointer equality
trait WeakTag[T <: AnyKind] extends AnyTag {
def tag: LightTypeTag
def closestClass: Class[_]
override final def toString: String = s"WeakTag[$tag]"
}
object WeakTag extends WeakTagInstances1 {
@inline def apply[T <: AnyKind: WeakTag]: WeakTag[T] = implicitly
def apply[T <: AnyKind](cls: Class[_], l: LightTypeTag): WeakTag[T] = {
new WeakTag[T] {
override val tag: LightTypeTag = l
override val closestClass: Class[_] = cls
}
}
implicit def weakTagFromTag[T <: AnyKind](implicit t: Tag[T]): WeakTag[T] = WeakTag(t.closestClass, t.tag)
}
trait WeakTagInstances1 {
implicit final def weakTagFromWeakTypeTag[T <: AnyKind](implicit l: LTag.Weak[T]): WeakTag[T] = WeakTag(classOf[Any], l.tag)
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/dottyreflection/FullDbInspector.scala
================================================
package izumi.reflect.dottyreflection
import izumi.reflect.internal.fundamentals.collections.IzCollections.toRich
import izumi.reflect.macrortti.LightTypeTagRef
import izumi.reflect.macrortti.LightTypeTagRef.*
import scala.collection.immutable.Queue
import scala.collection.mutable
import scala.quoted.*
object FullDbInspector {
def make(q: Quotes): FullDbInspector { val qctx: q.type } = new FullDbInspector(0) {
override val qctx: q.type = q
}
}
abstract class FullDbInspector(protected val shift: Int) extends InspectorBase {
import qctx.reflect._
def buildFullDb(typeRepr: TypeRepr): Map[AbstractReference, Set[AbstractReference]] = {
new Run(Inspector.make(qctx), mutable.HashSet.empty, mutable.HashSet.empty)
.inspectTypeReprToFullBases(typeRepr, onlyIndirect = false)
.iterator
.filterNot {
case (t, parent) =>
parent == t
}
.toMultimap
}
class Run(
inspector: Inspector { val qctx: FullDbInspector.this.qctx.type },
basesTermination: mutable.HashSet[Symbol],
toLambdaTermination: mutable.HashSet[Symbol]
) {
def inspectTypeReprToFullBases(tpe0: TypeRepr, onlyIndirect: Boolean): List[(AbstractReference, AbstractReference)] = {
val tpe = tpe0._dealiasSimplifiedFull
def selfRef(): AbstractReference = inspector.inspectTypeRepr(tpe)
tpe match {
case appliedType: AppliedType =>
extractBase(appliedType, selfRef(), onlyIndirect = onlyIndirect) ++ extractLambdaBase(appliedType, onlyIndirect = onlyIndirect)
case typeLambda: TypeLambda =>
val resultTypeParents = new Run(inspector.nextLam(typeLambda), basesTermination, toLambdaTermination).inspectTypeBoundsToFull(typeLambda.resType)
makeLambdaParents(selfRef(), resultTypeParents)
case a: AndType =>
inspectTypeReprToFullBases(a.left, onlyIndirect = false) ++ inspectTypeReprToFullBases(a.right, onlyIndirect = false)
case o: OrType =>
inspectTypeReprToFullBases(o.left, onlyIndirect = false) ++ inspectTypeReprToFullBases(o.right, onlyIndirect = false)
case typeRef: TypeRef =>
processSymbol(typeRef, selfRef(), onlyIndirect = onlyIndirect)
case _: ParamRef =>
// do not process type parameters for bases db
Nil
case termRef: TermRef =>
extractBase(termRef, selfRef(), onlyIndirect = onlyIndirect)
case b: TypeBounds =>
processTypeBounds(b)
case c: ConstantType =>
extractBase(c, selfRef(), onlyIndirect = onlyIndirect)
case t: ThisType =>
inspectTypeReprToFullBases(t.tref, onlyIndirect = onlyIndirect)
case r: Refinement =>
refinementInfoToParts(r.info).flatMap(inspectTypeBoundsToFull)
++ inspectTypeReprToFullBases(r.parent, onlyIndirect = onlyIndirect)
case other =>
log(s"FullDbInspector: UNSUPPORTED: $other")
extractBase(other, selfRef(), onlyIndirect = onlyIndirect)
}
}
// Equivalent to Scala 2 LightTypeTagImpl.makeLambdaOnlyBases
private def extractLambdaBase(appliedType: AppliedType, onlyIndirect: Boolean): List[(AbstractReference, AbstractReference)] = {
if (onlyIndirect || toLambdaTermination.isTerminatingClsSym(appliedType)) {
Nil
} else {
appliedType._etaExpand match {
case None => Nil
case Some(typeLambda) =>
toLambdaTermination.addTerminatingClsSym(appliedType)
val resultTypeParents = new Run(inspector.nextLam(typeLambda), basesTermination, toLambdaTermination)
.inspectTypeBoundsToFull(typeLambda.resType)
makeLambdaParents(inspector.inspectTypeRepr(typeLambda), resultTypeParents)
}
}
}
private def makeLambdaParents(
selfRef: AbstractReference,
resultTypeParents: List[(AbstractReference, AbstractReference)]
): List[(AbstractReference, AbstractReference)] = {
val selfL = selfRef.asInstanceOf[Lambda]
val out = resultTypeParents.flatMap {
case (child0, parent0) =>
val child = if (child0 == selfL.output) { // if child == typeLambda.resType, use typeLambda itself
selfL
} else {
child0
}
// For Scala 2: see LightTypeTagImpl.makeLambdaOnlyBases.makeLambdaParents
def maybeToLambda(parentOrChild: LightTypeTagRef): AbstractReference = parentOrChild match {
case l: Lambda =>
l
case applied: AppliedReference =>
val l = LightTypeTagRef.Lambda(selfL.input, applied)
if (l.someArgumentsReferenced) l else applied
}
val childMaybeAsLambda = maybeToLambda(child)
val parentMaybeAsLambda = maybeToLambda(parent0)
Seq(
(childMaybeAsLambda, parentMaybeAsLambda)
// you may debug by inserting some trash into the dbs:
// NameReference(SymName.SymTypeName(s"LEFT ${System.nanoTime()} before:$child after:$childMaybeAsLambda")) ->
// NameReference(SymName.SymTypeName(s"RIGHT before:$parent0 after:$parentMaybeAsLambda"))
)
}
out
}
private def processSymbol(r: TypeRef | ParamRef, selfRef: AbstractReference, onlyIndirect: Boolean): List[(AbstractReference, AbstractReference)] = {
r.typeSymbol match {
case s if s.isClassDef =>
extractBase(r, selfRef, onlyIndirect = onlyIndirect)
case s if s.isTypeDef =>
// println(r -> s -> r._underlying)
processTypeMemberWithTypeLambdaBounds(r, onlyIndirect = onlyIndirect)
case o =>
throw new RuntimeException(s"Unknown tree: ${o.getClass} $o $r ${o.tree} (pretty: ${o.tree.show})")
}
}
private def extractBase(tpe: TypeRepr, selfRef: AbstractReference, onlyIndirect: Boolean): List[(AbstractReference, AbstractReference)] = {
val argBasesRefs = tpe.typeArgs.flatMap {
case t if basesTermination.isTerminatingClsSym(t) => Nil
case t =>
inspectTypeBoundsToFull(t)
}
val baseTypes: List[TypeRepr] = tpe
.baseClasses
.map(tpe.baseType)
.filterNot(_ =:= tpe)
basesTermination.addTerminatingClsSym(tpe)
log(s"For `${tpe.show}` (onlyIndirect=$onlyIndirect) found base types ${baseTypes.map(_.show)}")
val recursiveParentRefs = baseTypes.flatMap {
case t if basesTermination.isTerminatingClsSym(t) => Nil
case t => inspectTypeReprToFullBases(t, onlyIndirect = true)
}
val directBaseRefs = if (onlyIndirect) {
Nil
} else {
baseTypes.map {
bt =>
val parentRef = inspector.inspectTypeRepr(bt)
(selfRef, parentRef)
}
}
val mainBasesRefs = recursiveParentRefs ::: directBaseRefs
argBasesRefs ::: mainBasesRefs
}
private def inspectTypeBoundsToFull(tpe: TypeRepr): List[(AbstractReference, AbstractReference)] = {
tpe._dealiasSimplifiedFull match {
case t: TypeBounds =>
processTypeBounds(t)
case t: TypeRepr =>
inspectTypeReprToFullBases(t, onlyIndirect = false)
}
}
private def processTypeBounds(tb: TypeBounds): List[(AbstractReference, AbstractReference)] = {
inspectTypeReprToFullBases(tb.hi, onlyIndirect = false) ++ inspectTypeReprToFullBases(tb.low, onlyIndirect = false)
}
private def processTypeMemberWithTypeLambdaBounds(t: TypeRef | ParamRef, onlyIndirect: Boolean): List[(AbstractReference, AbstractReference)] = {
t._underlying match {
// handle abstract higher-kinded type members specially,
// move their upper bound into inheritance db, because they
// will lose it after application. (Unlike proper type members)
case TypeBounds(_, tl0: TypeLambda) =>
val selfRef = inspector.inspectTypeRepr(t)
// include only upper bound: we discard the lower bound for abstract higher-kinded type members
val tl = tl0._dealiasSimplifiedFull
val hiTypeLambda = inspector.inspectTypeRepr(tl)
(selfRef, hiTypeLambda) :: replaceUpperBoundWithSelfInUpperBoundBases(selfRef, hiTypeLambda, tl, onlyIndirect = onlyIndirect)
case underlying @ TypeBounds(_, _) =>
val selfRef = inspector.inspectTypeRepr(t)
extractBase(underlying, selfRef, onlyIndirect = onlyIndirect)
// for opaque types
case underlying =>
inspectTypeReprToFullBases(underlying, onlyIndirect = onlyIndirect)
}
}
private def replaceUpperBoundWithSelfInUpperBoundBases(
selfRef: AbstractReference,
upperBound: AbstractReference,
upperBoundTpe: TypeRepr,
onlyIndirect: Boolean
): List[(AbstractReference, AbstractReference)] = {
val basesOfUpperBound = inspectTypeReprToFullBases(upperBoundTpe, onlyIndirect = onlyIndirect)
basesOfUpperBound.map {
case (k, v) if k == upperBound =>
// bases of upper bound are also bases of the abstract type
selfRef -> v
case kv =>
kv
}
}
extension (set: mutable.HashSet[Symbol]) {
private def addTerminatingClsSym(typeRepr: TypeRepr): Unit = {
typeRepr.classSymbol match {
case Some(clsSym) => set.add(clsSym)
case _ =>
}
}
private def isTerminatingClsSym(t: TypeRepr): Boolean = {
t.classSymbol match {
case Some(clsSym) => set.contains(clsSym)
case None => false
}
}
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/dottyreflection/InheritanceDbInspector.scala
================================================
package izumi.reflect.dottyreflection
import izumi.reflect.internal.fundamentals.collections.IzCollections.toRich
import izumi.reflect.macrortti.LightTypeTagRef
import izumi.reflect.macrortti.LightTypeTagRef.*
import scala.collection.immutable.Queue
import scala.collection.mutable
import scala.quoted.*
object InheritanceDbInspector {
def make(q: Quotes): InheritanceDbInspector { val qctx: q.type } = new InheritanceDbInspector(0) {
override val qctx: q.type = q
}
}
abstract class InheritanceDbInspector(protected val shift: Int) extends InspectorBase {
import qctx.reflect.*
def makeUnappliedInheritanceDb(typeRepr: TypeRepr): Map[NameReference, Set[NameReference]] = {
val tpe0 = typeRepr._dealiasSimplifiedFull
new Run(Inspector.make(qctx), mutable.HashSet.empty)
.makeUnappliedInheritanceDb(tpe0)
}
class Run(
inspector: Inspector { val qctx: InheritanceDbInspector.this.qctx.type },
termination: mutable.HashSet[Symbol]
) {
def makeUnappliedInheritanceDb(tpe0: TypeRepr): Map[NameReference, Set[NameReference]] = {
inspectTypeReprToUnappliedBases(tpe0, onlyIndirect = false)
.iterator
.filterNot {
case (parent, t) =>
parent == t
}
.toMultimap
}
private def inspectTypeReprToUnappliedBases(tpe0: TypeRepr, onlyIndirect: Boolean): List[(NameReference, NameReference)] = {
val allReferenceComponents = allTypeReferences(tpe0, onlyIndirect)
allReferenceComponents.iterator.flatMap(inspectTypeReprToUnappliedIndirectBases).toList
}
private def inspectTypeReprToUnappliedIndirectBases(i: TypeRepr): List[(NameReference, NameReference)] = {
val tpe = i._dealiasSimplifiedFull._resultType
val tpeRef = inspector.makeNameReferenceFromType(tpe)
tpeBases(tpeRef, tpe, onlyIndirect = false)
}
private def allTypeReferences(tpe0: TypeRepr, onlyIndirect: Boolean): mutable.Set[TypeRepr] = {
extension (t: TypeRepr) {
inline def dealiasPrepare: TypeRepr = {
t._dealiasSimplifiedFull._resultType
}
}
val inh = mutable.LinkedHashSet.empty[TypeRepr]
val tpeDealiased = tpe0.dealiasPrepare
def goExtractComponents(tpeRaw0: TypeRepr): Unit = {
val tpeRes = tpeRaw0.dealiasPrepare
val intersectionUnionMembers = breakRefinement(tpeRes)
if (intersectionUnionMembers.sizeIs == 1) {
inh += intersectionUnionMembers.head
}
(
tpeRes.typeArgs.iterator ++
intersectionUnionMembers.iterator.flatMap(_.typeArgs) ++
intersectionUnionMembers
).foreach(t => if (!inh.contains(t)) goExtractComponents(t))
}
goExtractComponents(tpe0)
inh.filterInPlace {
case _: ParamRef => false // do not process type parameters for inheritance db
case t if onlyIndirect => t != tpe0 && t != tpeDealiased && !isTerminatingClsSym(t)
case _ => true
}
inh
}
private def breakRefinement(tpe0: TypeRepr): collection.Set[TypeRepr] = {
val tpes = mutable.LinkedHashSet.empty[TypeRepr]
def go(t0: TypeRepr): Unit = t0._dealiasSimplifiedFull match {
case tpe: AndOrType =>
go(tpe.left)
go(tpe.right)
case r: Refinement =>
refinementInfoToParts(r.info).foreach(go)
go(r.parent)
case t =>
tpes += t
}
go(tpe0)
tpes
}
private def tpeBases(tpeRef: NameReference, typeRepr: TypeRepr, onlyIndirect: Boolean): List[(NameReference, NameReference)] = {
addTerminatingClsSym(typeRepr)
val typeReprBases = typeRepr
.baseClasses
.map(typeRepr.baseType)
val upperBoundBases = typeRepr match {
case t: TypeRef =>
t._underlying match {
// handle abstract higher-kinded type members specially,
// move their upper bound into inheritance db, because they
// will lose it after application. (Unlike proper type members)
case TypeBounds(_, tl: TypeLambda) =>
List(tl.resType._dealiasSimplifiedFull)
case _ =>
Nil
}
case _ =>
Nil
}
val allTypeReprBases = (upperBoundBases ::: typeReprBases)
.filterNot(_ =:= typeRepr)
val recursiveParentRefs = allTypeReprBases.flatMap {
case t if isTerminatingClsSym(t) => Nil
case t => inspectTypeReprToUnappliedBases(t, onlyIndirect = true)
}
val directBaseRefs = if (onlyIndirect) {
Nil
} else {
allTypeReprBases.filter(!_._takesTypeArgs).map(base => (tpeRef, inspector.makeNameReferenceFromType(base)))
}
recursiveParentRefs ::: directBaseRefs
}
private def addTerminatingClsSym(typeRepr: TypeRepr): Unit = {
typeRepr.classSymbol match {
case Some(clsSym) => termination.add(clsSym)
case _ =>
}
}
private def isTerminatingClsSym(t: TypeRepr): Boolean = {
t.classSymbol match {
case Some(clsSym) => termination.contains(clsSym)
case None => false
}
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/dottyreflection/Inspect.scala
================================================
package izumi.reflect.dottyreflection
import izumi.reflect.macrortti.LightTypeTag
import izumi.reflect.macrortti.LightTypeTag.ParsedLightTypeTag.SubtypeDBs
import izumi.reflect.thirdparty.internal.boopickle.PickleImpl
import scala.quoted.{Expr, Quotes, Type}
object Inspect {
inline def inspect[T <: AnyKind]: LightTypeTag = ${ inspectAny[T] }
inline def inspectStrong[T <: AnyKind]: LightTypeTag = ${ inspectStrong[T] }
def inspectAny[T <: AnyKind: Type](using qctx: Quotes): Expr[LightTypeTag] = {
inspectTypeRepr(qctx.reflect.TypeRepr.of[T])
}
def inspectTypeRepr(using qctx: Quotes)(typeRepr: qctx.reflect.TypeRepr): Expr[LightTypeTag] = {
val ltt = {
val ref = TypeInspections(typeRepr)
val fullDb = TypeInspections.fullDb(typeRepr)
val nameDb = TypeInspections.unappliedDb(typeRepr)
LightTypeTag(ref, fullDb, nameDb)
}
makeParsedLightTypeTagImpl(ltt)
}
def inspectStrong[T <: AnyKind: Type](using qctx: Quotes): Expr[LightTypeTag] = {
import qctx.reflect.*
val tpe = TypeRepr.of[T]
val owners = ReflectionUtil.getClassDefOwners(Symbol.spliceOwner)
if (ReflectionUtil.allPartsStrong(0, owners, Set.empty, tpe)) {
inspectAny[T]
} else {
report.errorAndAbort(s"Can't materialize LTag[$tpe]: found unresolved type parameters in $tpe")
}
}
def makeParsedLightTypeTagImpl(ltt: LightTypeTag)(using qctx: Quotes): Expr[LightTypeTag] = {
val serialized = ltt.serialize()
val hashCodeRef = serialized.hash
val strRef = serialized.ref
val strDBs = serialized.databases
InspectorBase.ifDebug {
def string2hex(str: String): String = str.toList.map(_.toInt.toHexString).mkString
println(s"${ltt.ref} => ${strRef.size} bytes, ${string2hex(strRef)}")
println(s"${SubtypeDBs.make(ltt.basesdb, ltt.idb)} => ${strDBs.size} bytes, ${string2hex(strDBs)}")
println(strDBs)
}
'{ LightTypeTag.parse(${ Expr(hashCodeRef) }, ${ Expr(strRef) }, ${ Expr(strDBs) }, ${ Expr(LightTypeTag.currentBinaryFormatVersion) }) }
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/dottyreflection/Inspector.scala
================================================
package izumi.reflect.dottyreflection
import izumi.reflect.macrortti.{LightTypeTagInheritance, LightTypeTagRef}
import izumi.reflect.macrortti.LightTypeTagRef.*
import izumi.reflect.macrortti.LightTypeTagRef.SymName.SymTypeName
import scala.annotation.{tailrec, targetName}
import scala.collection.immutable.Queue
import scala.quoted.{Quotes, Type}
import scala.reflect.Selectable.reflectiveSelectable
object Inspector {
case class LamParam(name: String, index: Int, depth: Int, arity: Int)(val qctx: Quotes)(val tpe: qctx.reflect.TypeRepr) {
def asParam = SymName.LambdaParamName(index, depth, arity) // s"$depth:$index/$arity")
// this might be useful for debugging
// def asParam = LambdaParameter(s"$depth:$index/$arity:$name")
override def toString: String = s"[($name: $tpe) = $asParam]"
}
case class LamContext(params: List[LamParam])
def make(q: Quotes): Inspector { val qctx: q.type } = new Inspector(0, Queue.empty) {
override val qctx: q.type = q
}
}
abstract class Inspector(protected val shift: Int, val context: Queue[Inspector.LamContext]) extends InspectorBase {
import qctx.reflect._
def next(newContext: Option[Inspector.LamContext] = None): Inspector { val qctx: Inspector.this.qctx.type } =
new Inspector(shift + 1, newContext.fold(this.context)(this.context :+ _)) {
val qctx: Inspector.this.qctx.type = Inspector.this.qctx
}
def nextLam(lambda: TypeLambda): Inspector { val qctx: Inspector.this.qctx.type } = {
val params = lambda
.paramNames
.zipWithIndex
.map {
case (nme, idx) =>
Inspector.LamParam(nme, idx, context.size, lambda.paramNames.size)(qctx)(lambda.param(idx))
}
.toList
next(Some(Inspector.LamContext(params)))
}
def buildTypeRef(typeRepr: TypeRepr): AbstractReference = {
log(s" -------- about to inspect ${typeRepr.show(using Printer.TypeReprStructure)} ($typeRepr) --------")
val res = inspectTypeRepr(typeRepr)
log(s" -------- done inspecting ${typeRepr.show(using Printer.TypeReprStructure)} ($typeRepr) --------")
res
}
private[dottyreflection] def inspectTypeRepr(tpe0: TypeRepr, outerTypeRef: Option[TypeRef] = None): AbstractReference = {
val tpe = tpe0._dealiasSimplifiedFull
if (context.flatMap(_.params.map(_.tpe)).toSet.contains(tpe0)) {
assert(tpe == tpe0)
assert(tpe match {
case _: ParamRef =>
true
case _ =>
false
})
}
// log(s"inspectTypeRepr tpe=$tpe outerTypeRef=$outerTypeRef")
tpe match {
case a: AnnotatedType =>
inspectTypeRepr(a.underlying)
case appliedType: AppliedType =>
val tycon: TypeRepr = appliedType.tycon._dealiasSimplifiedFull
val args0: List[TypeRepr] = appliedType.args
args0 match {
case Nil =>
makeNameReferenceFromType(tycon)
case _ =>
val symbolVariances: List[Variance] = tycon.typeSymbol.declaredTypes.collect {
case s if s.isTypeParam => extractVariance(s)
}
val variances: List[Variance] = if (symbolVariances.sizeCompare(appliedType.args) < 0) {
tycon match {
case typeParamRef: ParamRef =>
typeParamRef._underlying match {
case TypeBounds(_, hi) =>
hi._paramVariancesIfHKTypeLambda.fold(Nil)(_.map(extractVariance))
case _ =>
Nil
}
case _ =>
Nil
}
} else {
symbolVariances
}
val nameRef = makeNameReferenceFromType(tycon)
val args = args0
.iterator
.zipAll(variances, null.asInstanceOf[TypeRepr], Variance.Invariant)
.takeWhile(_._1 != null)
.map(next().inspectTypeParam(_, _)).toList
FullReference(symName = nameRef.symName, parameters = args, prefix = nameRef.prefix)
}
case l: TypeLambda =>
val inspector = nextLam(l)
val resType = inspector.inspectTypeRepr(l.resType)
val paramNames = inspector.context.last.params.map(_.asParam)
LightTypeTagRef.Lambda(paramNames, resType)
case t: ThisType =>
next().inspectTypeRepr(t.tref)
case a: AndType =>
val elements = flattenInspectAnd(a)
if (elements.sizeIs == 1) {
elements.head
} else {
IntersectionReference(elements)
}
case o: OrType =>
val elements = flattenInspectOr(o)
if (elements.sizeIs == 1) {
elements.head
} else {
UnionReference(elements)
}
case p: ParamRef =>
makeNameReferenceFromType(p)
case term: TermRef =>
makeNameReferenceFromType(term)
case r: TypeRef =>
next().inspectSymbol(r.typeSymbol, Some(r), Some(r))
case tb: TypeBounds =>
inspectBounds(outerTypeRef, tb)
case constant: ConstantType =>
makeNameReferenceFromType(constant)
case ref: Refinement =>
next().inspectRefinements(ref)
case lazyref if lazyref.getClass.getName.contains("LazyRef") => // upstream bug seems like
log(s"TYPEREPR UNSUPPORTED: LazyRef occured $lazyref")
NameReference(SymName.SymTypeName("???"))
case o =>
log(s"TYPEREPR UNSUPPORTED: $o")
throw new RuntimeException(s"TYPEREPR, UNSUPPORTED: ${o.getClass} - $o")
}
}
private def inspectRefinements(ref: Refinement): AbstractReference = {
val (refinements, nonRefinementParent) = flattenRefinements(ref)
val parentRef = next().inspectTypeRepr(nonRefinementParent)
val refinementDecls = refinements.map {
case (_, name, ByNameType(tpe)) => // def x(): Int
RefinementDecl.Signature(name, Nil, next().inspectTypeRepr(tpe).asInstanceOf[AppliedReference])
case (_, name, m0: MethodOrPoly) => // def x(i: Int): Int; def x[A](a: A): A
// FIXME as of version 2.3.0 RefinementDecl.Signature model is broken
// it doesn't support either multiple parameter lists or type parameters
// on methods, so this is a hacky to avoid fixing that for now.
def squashMethodIgnorePolyType(m: TypeRepr): (List[TypeRepr], TypeRepr) = {
m match {
case p: PolyType =>
squashMethodIgnorePolyType(p.resType)
case m: MethodType =>
val inputs = m.paramTypes
val (inputs2, res) = squashMethodIgnorePolyType(m.resType)
(inputs ++ inputs2, res)
case tpe =>
(Nil, tpe)
}
}
val (inputTpes, resType) = squashMethodIgnorePolyType(m0)
val inputRefs = inputTpes.map(next().inspectTypeRepr(_).asInstanceOf[AppliedReference])
val outputRef = next().inspectTypeRepr(resType).asInstanceOf[AppliedReference]
RefinementDecl.Signature(name, inputRefs, outputRef)
case (_, name, bounds: TypeBounds) => // type T = Int
val boundaries = next().inspectBoundsImpl(bounds)
val res = boundaries match {
case Boundaries.Defined(low, high) if high == low =>
// concrete type member: type T = Int (can't distinguish between equal-bounded type and alias inside refinements in dotty)
high
case definedBoundaries =>
// abstract type member: type T >: Int <: AnyVal
NameReference(SymName.SymTypeName(name), definedBoundaries, None)
}
RefinementDecl.TypeMember(name, res)
case (_, name, tpe) => // val t: Int
RefinementDecl.Signature(name, Nil, next().inspectTypeRepr(tpe).asInstanceOf[AppliedReference])
}
val ohOh = parentRef.asInstanceOf[AppliedReference]
LightTypeTagRef.Refinement(ohOh, refinementDecls.toSet)
}
private def inspectBounds(outerTypeRef: Option[TypeRef], tb: TypeBounds): AbstractReference = {
log(s"inspectBounds: found TypeBounds $tb outer=$outerTypeRef")
val boundaries = inspectBoundsImpl(tb)
if (outerTypeRef.isEmpty) {
// Boundaries in parameters always stand for wildcards even though Scala3 eliminates wildcards
WildcardReference(boundaries)
} else {
// Type projections like A#S2 don't get dealiased with .dealias for some reason, so we dereference them manually here
val outerTpe = outerTypeRef.get
boundaries match {
case Boundaries.Defined(bottom, top) if outerTpe.typeSymbol.isAliasType && top == bottom =>
// type projection, return underlying
top
case _ =>
// abstract type, return TpeName|LowerBound..UpperBound
// Boundaries which are not parameters are named types (e.g. type members) and are NOT wildcards
// if hi and low boundaries are defined and distinct, type is not reducible to one of them
val nameRef = makeNameReferenceFromType(outerTypeRef.get).copy(boundaries = boundaries)
invertTypeMemberWithTypeLambdaBounds(nameRef)
}
}
}
private def invertTypeMemberWithTypeLambdaBounds(abstractReference: AbstractReference): AbstractReference = abstractReference match {
case NameReference(symName, Boundaries.Defined(bottom @ _, LightTypeTagRef.Lambda(input, realTop @ _)), prefix) =>
// We throw away both upper and lower boundaries
// Upper boundaries we'll recover later in fulldb and inheritancedb
// But lower boundaries we don't recover
log(s"invertTypeMemberWithTypeLambdaBounds: found symName=$symName, input=$input")
LightTypeTagRef.Lambda(input, FullReference(symName, input.map(p => TypeParam(NameReference(p), Variance.Invariant)), prefix))
case other =>
other
}
private def inspectBoundsImpl(tb: TypeBounds): Boundaries = {
val hi = next().inspectTypeRepr(tb.hi)
val low = next().inspectTypeRepr(tb.low)
if (hi == LightTypeTagInheritance.tpeAny && low == LightTypeTagInheritance.tpeNothing) {
Boundaries.Empty
} else {
Boundaries.Defined(low, hi)
}
}
private[dottyreflection] def inspectSymbol(symbol: Symbol, outerTypeRef: Option[TypeRef], prefixSource: Option[NamedType]): AbstractReference = {
symbol match {
case s if s.isClassDef || s.isValDef || s.isBind =>
log(s"inspectSymbol: Found Cls=${s.isClassDef} Val=${s.isValDef} Bind=${s.isBind} symbol $s")
makeNameReferenceFromSymbol(symbol, prefixSource)
case s if s.isTypeDef =>
val rhs = outerTypeRef match {
case Some(r) if r.isOpaqueAlias => r._underlying
case _ => s.typeRef._underlying
}
log(s"inspectSymbol: Found TypeDef symbol $s (rhs=$rhs)")
next().inspectTypeRepr(rhs, outerTypeRef)
case s if s.isDefDef =>
// We don't support method types, but if we do in the future,
// Something like `s.typeRef.translucentSuperType match { case MethodType(_, params, resultType) => (params, resultType) }`
// should get the result type & params
log(s"UNEXPECTED METHOD TYPE, METHOD TYPES UNSUPPORTED: $symbol / ${symbol.tree} / ${s.getClass}")
throw new RuntimeException(s"UNEXPECTED METHOD TYPE, METHOD TYPES UNSUPPORTED: $symbol / ${symbol.tree} / ${s.getClass}")
case o => // Should not happen according to documentation of `.tree` method
// still no access to relevant types
log(s"SYMBOL TREE, UNSUPPORTED: $symbol / $o / ${o.getClass}")
throw new RuntimeException(s"SYMBOL TREE, UNSUPPORTED: $symbol / $o / ${o.getClass}")
}
}
private def inspectTypeParam(tpe: TypeRepr, variance: Variance): TypeParam = {
tpe match {
case t: TypeBounds => // wildcard
TypeParam(inspectBounds(outerTypeRef = None, tb = t), variance)
case t: TypeRepr =>
TypeParam(inspectTypeRepr(t), variance)
}
}
private def extractVariance(t: Symbol): Variance = {
extractVariance(t.flags)
}
@targetName("extractVarianceFlags")
private def extractVariance(flags: Flags): Variance = {
if (flags.is(Flags.Covariant)) {
Variance.Covariant
} else if (flags.is(Flags.Contravariant)) {
Variance.Contravariant
} else {
Variance.Invariant
}
}
private def flattenInspectAnd(and: AndType): Set[AppliedReferenceExceptIntersection] = {
flattenAnd(and).toSet.map(inspectTypeRepr(_)).flatMap {
case i: IntersectionReference => i.refs
case other: AppliedReferenceExceptIntersection => Set(other)
case _: LightTypeTagRef.Lambda => Set.empty
}
}
private def flattenInspectOr(or: OrType): Set[AppliedReferenceExceptUnion] =
flattenOr(or).toSet.map(inspectTypeRepr(_)).flatMap {
case i: UnionReference => i.refs
case other: AppliedReferenceExceptUnion => Set(other)
case _: LightTypeTagRef.Lambda => Set.empty
}
private[dottyreflection] def makeNameReferenceFromType(t: TypeRepr): NameReference = {
t match {
case ref: TypeRef =>
log(s"make name reference from type $ref termSymbol=${ref.termSymbol}")
makeNameReferenceFromSymbol(ref.typeSymbol, Some(ref))
case term: TermRef =>
log(s"make name reference from term $term")
makeNameReferenceFromSymbol(term.termSymbol, Some(term))
case t: ParamRef =>
log(s"make name reference from paramRef $t")
val isInContext = context.flatMap(_.params.map(_.tpe)).contains(t)
if (isInContext) {
// assert(isInContext, s"${context.flatMap(_.params.map(_.tpe)).map(t => t)} must contain $t")
val candidates = context.reverse.flatMap(_.params).filter(_.tpe == t)
val contextParam = candidates.head
locally {
val paramName = t.binder.asInstanceOf[LambdaType].paramNames(t.paramNum).toString
assert(contextParam.name == paramName, s"$contextParam should match $paramName")
}
NameReference(contextParam.asParam, Boundaries.Empty, None)
} else {
val lt = t.binder.asInstanceOf[LambdaType]
NameReference(SymName.LambdaParamName(t.paramNum, -1, lt.paramNames.size), Boundaries.Empty, None)
}
case constant: ConstantType =>
log(s"make name reference from ConstantType $constant")
NameReference(SymName.SymLiteral(constant.constant.value), Boundaries.Empty, prefix = None)
case ref =>
log(s"make name reference from what? $ref ${ref.getClass} ${ref.termSymbol}")
makeNameReferenceFromSymbol(ref.typeSymbol, None)
}
}
@tailrec
private[dottyreflection] final def makeNameReferenceFromSymbol(symbol: Symbol, prefixSource1: Option[NamedType]): NameReference = {
def default: NameReference = {
val (symName, prefixSource2) = if (symbol.isTerm || symbol.isBind || symbol.isValDef) {
(SymName.SymTermName(symbol.fullName), symbol.termRef)
} else if (symbol.flags.is(Flags.Module)) { // Handle ModuleClasses (can creep in from ThisType)
(SymName.SymTermName(symbol.companionModule.fullName), symbol.companionModule.termRef)
} else {
(SymName.SymTypeName(symbol.fullName), symbol.termRef)
}
val prefix = getPrefixFromQualifier(prefixSource1.getOrElse(prefixSource2))
NameReference(symName, Boundaries.Empty, prefix)
}
if (symbol.isBind) { log(s"inspectSymbol: Found Bind symbol $symbol") }
if (symbol.isValDef) {
log(s"inspectSymbol: Found ValDef symbol $symbol")
symbol.typeRef._underlying match {
case constant: ConstantType => // constant type vals are aliases to constant types
makeNameReferenceFromType(constant)
case t: TermRef => // singleton type vals are aliases to their singleton
makeNameReferenceFromSymbol(t.termSymbol, Some(t))
case other =>
log(s"inspectSymbol: found UNKNOWN symbol in ValDef $other")
default
}
} else {
default
}
}
private def getPrefixFromQualifier(tpe: NamedType): Option[AppliedReference] = {
tpe.qualifier match {
// Support https://github.com/zio/izumi-reflect/issues/35
// consider class member's this-prefix to be the defining template, not the most specific prefix from where the call is happening
case _: ThisType | _: SuperType | _: RecursiveThis =>
val s = if (!tpe.termSymbol.isNoSymbol) {
tpe.termSymbol
} else {
tpe.typeSymbol
}
val maybeOwner = s.maybeOwner
if (!maybeOwner.exists || maybeOwner.isNoSymbol || maybeOwner.isPackageDef || maybeOwner.isDefDef || maybeOwner.isTypeDef || maybeOwner.isLocalDummy) {
None
} else {
inspectSymbol(maybeOwner, None, None) match {
case a: AppliedReference =>
log(s"Constructed prefix=$a from owner=$maybeOwner")
Some(a)
case _ =>
None
}
}
case prefix =>
val typeSymbol = prefix.typeSymbol
if (!typeSymbol.exists || typeSymbol.isNoSymbol || typeSymbol.isPackageDef || typeSymbol.isDefDef || typeSymbol.isTypeDef || typeSymbol.isLocalDummy) {
None
} else {
inspectTypeRepr(prefix) match {
case reference: AppliedReference =>
log(s"Constructed prefix=$reference from prefix=$prefix")
Some(reference)
case _ => None
}
}
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/dottyreflection/InspectorBase.scala
================================================
package izumi.reflect.dottyreflection
import scala.quoted.Quotes
trait InspectorBase extends ReflectionUtil {
val qctx: Quotes
import qctx.reflect._
protected def shift: Int
// FIXME reimplement TrivialMacroLogger for Scala 3
inline def debug: debug = valueOf[debug]
final type debug = false
// println instead of report.info because report.info eats all the subsequent report.info's after first.
inline final protected def logStart(inline s: String): Unit = {
inline if (debug) println(" " * shift + currentPositionStr + s)
}
inline final protected def log(inline s: String): Unit = {
inline if (debug) println(" " * shift + currentPositionStr + " -> " + s)
}
inline final protected def logTpeAttrs[T](inline typeRepr: TypeRepr): Unit = {
inline if (debug) {
val tree = TypeTree.of(using typeRepr.asType)
val symbol = tree.symbol
System
.err.println(
currentPositionStr + ": " +
s"Attrs[${tree.show}]: type=${symbol.isType}, term=${symbol.isTerm}, packageDef=${symbol.isPackageDef}, classDef=${symbol.isClassDef}, typeDef=${symbol.isValDef}, defdef=${symbol.isDefDef}, bind=${symbol.isBind}, nosymbol=${symbol.isNoSymbol}"
)
}
}
private def currentPositionStr: String = {
val pos = qctx.reflect.Position.ofMacroExpansion
s"${pos.sourceFile.name}:${pos.endLine}"
}
}
object InspectorBase {
private[reflect] inline def ifDebug[A](inline f: => Unit): Unit = {
inline if (valueOf[InspectorBase#debug]) {
f
}
}
private[reflect] inline def log(inline shift: Int, s: String): Unit = {
inline if (valueOf[InspectorBase#debug]) println(" " * shift + " -> " + s)
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/dottyreflection/ReflectionUtil.scala
================================================
package izumi.reflect.dottyreflection
import scala.annotation.{nowarn, tailrec, unused}
import scala.collection.immutable.Queue
import scala.quoted.Quotes
private[dottyreflection] trait ReflectionUtil { this: InspectorBase =>
import qctx.reflect.*
private final lazy val ignoredInIntersections0: Set[TypeRepr] = {
Set(
defn.AnyClass.typeRef,
defn.MatchableClass.typeRef,
defn.AnyRefClass.typeRef,
defn.ObjectClass.typeRef
)
}
def ignoredInIntersections(repr: qctx.reflect.TypeRepr): Boolean = {
ignoredInIntersections0.exists(_ =:= repr)
}
def ignoredInUnions(repr: qctx.reflect.TypeRepr): Boolean = {
repr =:= defn.NothingClass.typeRef
}
protected final def flattenAnd(tpe: TypeRepr): List[TypeRepr] =
tpe.dealias match {
case AndType(lhs, rhs) => flattenAnd(lhs) ++ flattenAnd(rhs)
case _ => List(tpe)
}
protected final def flattenOr(tpe: TypeRepr): List[TypeRepr] =
tpe.dealias match {
case OrType(lhs, rhs) => flattenOr(lhs) ++ flattenOr(rhs)
case _ => List(tpe)
}
protected final def intersectionUnionRefinementClassPartsOf(tpe: TypeRepr): List[TypeRepr] = {
tpe.dealias match {
case AndType(lhs, rhs) =>
intersectionUnionRefinementClassPartsOf(lhs) ++ intersectionUnionRefinementClassPartsOf(rhs)
case OrType(lhs, rhs) =>
intersectionUnionRefinementClassPartsOf(lhs) ++ intersectionUnionRefinementClassPartsOf(rhs)
case refinement: Refinement =>
intersectionUnionRefinementClassPartsOf(refinement.parent)
case _ =>
List(tpe)
}
}
protected final def refinementInfoToParts(tpe0: TypeRepr): List[TypeRepr] = {
tpe0 match {
case ByNameType(tpe) =>
refinementInfoToParts(tpe)
case MethodType(_, args, res) =>
args.flatMap(refinementInfoToParts) ++ refinementInfoToParts(res)
case PolyType(_, tbounds, res) =>
// FIXME we need to do FullDbInspector.inspectTypeReprToFullBases.lambdify/LightTypeTagImpl.makeLambdaOnlyBases.makeLambdaParents
// to wrap the unresolved type params in `res` into a lambda.
// As is, if type parameters are used in `res`, we'll add lots of trash types into db
tbounds.flatMap { case TypeBounds(lo, hi) => List(lo, hi) } ++ refinementInfoToParts(res)
case tpe =>
List(tpe)
}
}
protected final def flattenRefinements(ref: Refinement): (Queue[(Symbol, String, TypeRepr)], TypeRepr) = {
val refinementDecl = (ref.typeSymbol, ref.name, ref.info)
ref.parent match {
case innerRefinement: Refinement =>
val (innerRefs, nonRefinementParent) = flattenRefinements(innerRefinement)
(innerRefs :+ refinementDecl, nonRefinementParent)
case nonRefinementParent =>
(Queue(refinementDecl), nonRefinementParent)
}
}
protected final def allPartsStrong(outerOwnerClassDefs: Set[Symbol], typeRepr: TypeRepr): Boolean = {
ReflectionUtil.allPartsStrong(using qctx)(shift, outerOwnerClassDefs, Set.empty, typeRepr)
}
protected final def getClassDefOwners(symbol: Symbol): Set[Symbol] = {
ReflectionUtil.getClassDefOwners(using qctx)(symbol)
}
import ReflectionUtil.reflectiveUncheckedNonOverloadedSelectable
import InternalContext.InternalContext
extension (symbol: Symbol) {
protected final def _info: TypeRepr = {
symbol.asInstanceOf[InternalSymbol].denot(qctx._ctx).info(qctx._ctx)
}
}
extension (typeRef: TypeRef | ParamRef) {
protected final def _underlying: TypeRepr = {
// This works as a substitution for `TypeRef#underlying` call,
// but I'm not sure if it's a reliable substitution.
// typeRef.typeSymbol.owner._typeRef.memberType(typeRef.typeSymbol)
// No, It's not a reliable substitution. When used on a TypeParamRef it returns Any instead of the underlying TypeBounds
// https://github.com/lampepfl/dotty/issues/15799
// val underlying = typeRef
// .getClass.getMethods.collect { case m if m.getName == "underlying" => m }.head.invoke(
// typeRef,
// qctx.getClass.getMethods.collect { case m if m.getName == "ctx" => m }.head.invoke(qctx)
// )
// underlying.asInstanceOf[TypeRepr]
typeRef.asInstanceOf[InternalTypeRefOrParamRef].underlying(qctx._ctx)
}
}
extension (appliedType: AppliedType) {
protected final def _etaExpand: Option[TypeLambda] = {
etaExpandImpl(appliedType.tycon._dealiasSimplifiedFull, tparams0 => tparams0.sizeCompare(appliedType.args) < 0)
}
}
@nowarn("msg=anonymous class definition will be duplicated")
inline private final def etaExpandImpl(tycon: TypeRepr, inline checkIfPartialApplication: List[TypeRepr] => Boolean): Option[TypeLambda] = {
val tparams0: List[TypeRepr] = tycon.typeSymbol.declaredTypes.collect { case s if s.isTypeParam => s._info }
val tparams: List[TypeRepr] = if (checkIfPartialApplication(tparams0)) {
tycon match {
case typeParamRef: ParamRef =>
typeParamRef._underlying match {
case TypeBounds(_, hi: LambdaType) =>
hi.paramTypes
case _ =>
Nil
}
case _ =>
Nil
}
} else {
tparams0
}
if (tparams.nonEmpty) {
val indices = tparams.indices
Some(
TypeLambda(
paramNames = indices.iterator.map(_.toString).toList,
boundsFn = _ => tparams.map { case tb: TypeBounds => tb; case t => TypeBounds(t, t) },
bodyFn = tl => AppliedType(tycon, indices.iterator.map(tl.param(_)).toList)
)
)
} else None
}
extension (typeRepr: TypeRepr) {
protected final def _resultType: TypeRepr = {
typeRepr match {
case l: LambdaType => l.resType
case _ => typeRepr
}
}
protected final def _takesTypeArgs: Boolean = {
typeRepr match {
case _: LambdaType => true
case tref: TypeRef =>
val tsym = tref.typeSymbol
tsym.isType && tsym.declaredTypes.exists(_.isTypeParam)
case _ => false
}
}
protected final def _paramVariancesIfHKTypeLambda: Option[List[Flags]] = {
try {
val params = typeRepr.asInstanceOf[InternalHKTypeLambda].typeParams
val flags = params.map(_.paramVariance(qctx._ctx))
Some(flags)
} catch {
case _: NoSuchMethodException => None
}
}
protected final def _etaExpandTypeRef: TypeRepr = {
typeRepr match {
case tref: TypeRef =>
etaExpandImpl(tref, _ => false) match {
case Some(typeLambda) => typeLambda
case None => typeRepr
}
case _ => typeRepr
}
}
@tailrec
protected final def _dealiasSimplifiedFull: TypeRepr = {
// val res = typeRepr.dealias.simplified
// simplified does everything below functions do, with exception of `_removeTautologicalUnions` for some reason
// All of these would be more useful, if not for forced type simplification on implicit macro
// - https://github.com/lampepfl/dotty/issues/17544
val res = typeRepr.dealias._removeTautologicalIntersections._removeTautologicalUnions._simplifyMatchCase
if (res.asInstanceOf[AnyRef] eq typeRepr.asInstanceOf[AnyRef]) {
res
} else {
res._dealiasSimplifiedFull
}
}
// Calling .simplified will remove too many intersections - we only want to remove those with Any/AnyRef/Object/Matchable
@tailrec private def _removeTautologicalIntersections: TypeRepr = {
typeRepr match {
case AndType(a, b) =>
if (ignoredInIntersections(a)) {
b._removeTautologicalIntersections
} else if (ignoredInIntersections(b)) {
a._removeTautologicalIntersections
} else {
removeTautologicalIntersectionsNonTailRec(a, b)
}
case _ =>
typeRepr
}
}
private def removeTautologicalIntersectionsNonTailRec(a: TypeRepr, b: TypeRepr): TypeRepr = {
val a0 = a._removeTautologicalIntersections
val b0 = b._removeTautologicalIntersections
if ((a.asInstanceOf[AnyRef] ne a0.asInstanceOf[AnyRef]) || (b.asInstanceOf[AnyRef] ne b0.asInstanceOf[AnyRef])) {
AndType(a0, b0)
} else {
typeRepr
}
}
@tailrec private def _removeTautologicalUnions: TypeRepr = {
typeRepr match {
case OrType(a, b) =>
if (ignoredInUnions(a)) {
b._removeTautologicalUnions
} else if (ignoredInUnions(b)) {
a._removeTautologicalUnions
} else {
removeTautologicaUnionsNonTailRec(a, b)
}
case _ =>
typeRepr
}
}
private def removeTautologicaUnionsNonTailRec(a: TypeRepr, b: TypeRepr): TypeRepr = {
val superA = ignoredInIntersections(a)
val superB = ignoredInIntersections(b)
if (superA && superB) {
(if (a <:< b) b else a)._removeTautologicalUnions
} else if (superA) {
a
} else if (superB) {
b
} else {
val a0 = a._removeTautologicalUnions
val b0 = b._removeTautologicalUnions
if ((a.asInstanceOf[AnyRef] ne a0.asInstanceOf[AnyRef]) || (b.asInstanceOf[AnyRef] ne b0.asInstanceOf[AnyRef])) {
AndType(a0, b0)
} else {
typeRepr
}
}
}
inline private def _simplifyMatchCase: TypeRepr = {
typeRepr match {
case _: MatchCase | _: MatchType =>
// no other way to evaluate a match type other than calling simplified,
// even though that'll also cause a collapse of tautological intersections
// other than with Any/AnyRef/Object/Matchable
typeRepr.simplified
case _ =>
typeRepr
}
}
}
extension (qctx: Quotes) {
final def _ctx: InternalContext = qctx.asInstanceOf[{ def ctx: InternalContext }].ctx
}
type InternalTypeRefOrParamRef = {
def underlying(ctx: InternalContext): TypeRepr
}
type InternalHKTypeLambda = {
val typeParams: List[InternalLambdaParam]
}
type InternalLambdaParam = {
def paramVariance(ctx: InternalContext): Flags
}
type InternalSymbol = {
def denot(ctx: InternalContext): InternalDenot
}
type InternalDenot = {
def info(ctx: InternalContext): TypeRepr
}
object InternalContext {
opaque type InternalContext = Any
}
}
private[reflect] object ReflectionUtil {
private[reflect] inline implicit def reflectiveUncheckedNonOverloadedSelectable(x: Any): UncheckedNonOverloadedSelectable = new UncheckedNonOverloadedSelectable(x)
/**
* Returns true if the given type contains no type parameters
* (this means the type is not "weak" https://stackoverflow.com/questions/29435985/weaktypetag-v-typetag)
*/
private[reflect] def allPartsStrong(
using qctx: Quotes
)(shift: Int,
outerOwnerClassDefs: Set[qctx.reflect.Symbol],
outerLambdas: Set[qctx.reflect.TypeRepr],
typeRepr: qctx.reflect.TypeRepr
): Boolean = {
import qctx.reflect.*
typeRepr.dealias match {
case x if topLevelWeakType(outerOwnerClassDefs, outerLambdas, x) => false
case AppliedType(tpe, args) =>
allPartsStrong(shift, outerOwnerClassDefs, outerLambdas, tpe) && args.forall(allPartsStrong(shift, outerOwnerClassDefs, outerLambdas, _))
case AndType(lhs, rhs) => allPartsStrong(shift, outerOwnerClassDefs, outerLambdas, lhs) && allPartsStrong(shift, outerOwnerClassDefs, outerLambdas, rhs)
case OrType(lhs, rhs) => allPartsStrong(shift, outerOwnerClassDefs, outerLambdas, lhs) && allPartsStrong(shift, outerOwnerClassDefs, outerLambdas, rhs)
case TypeRef(tpe, _) => allPartsStrong(shift, outerOwnerClassDefs, outerLambdas, tpe)
case TermRef(tpe, _) => allPartsStrong(shift, outerOwnerClassDefs, outerLambdas, tpe)
case ThisType(tpe) => allPartsStrong(shift, outerOwnerClassDefs, outerLambdas, tpe)
case NoPrefix() => true
case TypeBounds(lo, hi) => allPartsStrong(shift, outerOwnerClassDefs, outerLambdas, lo) && allPartsStrong(shift, outerOwnerClassDefs, outerLambdas, hi)
case lam @ TypeLambda(_, _, body) => allPartsStrong(shift, outerOwnerClassDefs, outerLambdas + lam, body)
case Refinement(parent, _, tpe) => allPartsStrong(shift, outerOwnerClassDefs, outerLambdas, tpe) && allPartsStrong(shift, outerOwnerClassDefs, outerLambdas, parent)
case ByNameType(tpe) => allPartsStrong(shift, outerOwnerClassDefs, outerLambdas, tpe)
case strange =>
InspectorBase.log(shift, s"Got unknown type component when checking strength: $strange")
true
}
}
private[reflect] def topLevelWeakType(
using qctx: Quotes
)(outerOwnerClassDefs: Set[qctx.reflect.Symbol],
outerLambdas: Set[qctx.reflect.TypeRepr],
typeRepr: qctx.reflect.TypeRepr
): Boolean = {
import qctx.reflect.*
typeRepr match {
case x if x.typeSymbol.isTypeParam =>
x match {
case t: ParamRef if outerLambdas.contains(t.binder) => false
case _ => true
}
// we regard abstract types like T in trait X { type T; Tag[this.T] } - when we are _inside_ the definition template
// as 'type parameters' too. So that you could define `implicit def tagForT: Tag[this.T]` and the tag would be resolved
// to this implicit correctly, instead of generating a useless `X::this.type::T` tag.
// TODO: Due to https://github.com/lampepfl/dotty/issues/16107 not being fixed we have to make sure we're actually
// inside the definition of the this-type prefix to count it as 'weak' - unlike Scala 2 we're not protected
// from this-types leaking in and have to carry the owner chain here - until that issue is fixed.
case x @ TypeRef(ThisType(prefix), _) if x.typeSymbol.isAbstractType && !x.typeSymbol.isClassDef && outerOwnerClassDefs.contains(prefix.typeSymbol) =>
true
case _ => false
}
}
private[reflect] def getClassDefOwners(using qctx: Quotes)(symbol: qctx.reflect.Symbol): Set[qctx.reflect.Symbol] = {
Iterator
.iterate(symbol) {
s =>
val owner = s.owner
if (owner == null || owner.isNoSymbol || owner == qctx.reflect.defn.RootClass) {
null.asInstanceOf[qctx.reflect.Symbol]
} else {
owner
}
}
.takeWhile(_ ne null)
.filter(s => s.isClassDef && !s.isAbstractType)
.toSet
}
private[reflect] final class UncheckedNonOverloadedSelectable(private val selectable: Any) extends AnyVal with Selectable {
inline def selectDynamic(name: String): Any = {
applyDynamic(name)()
}
def applyDynamic(name: String, @unused paramTypes: Class[_]*)(args: Any*): Any = {
val cls = selectable.getClass
val method = {
if (args.isEmpty) {
cls.getMethod(name)
} else {
cls.getMethods.collectFirst { case m if m.getName == name => m } match {
case Some(m) => m
case None => throw new NoSuchMethodException(s"No method named `$name` found in class `$cls`")
}
}
}
method.invoke(selectable, args*)
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/dottyreflection/TypeInspections.scala
================================================
package izumi.reflect.dottyreflection
import izumi.reflect.macrortti.LightTypeTagRef.{AbstractReference, NameReference}
import scala.quoted.{Quotes, Type}
import scala.collection.immutable.Queue
object TypeInspections {
def apply(using qctx: Quotes)(typeRepr: qctx.reflect.TypeRepr): AbstractReference = {
Inspector.make(qctx).buildTypeRef(typeRepr)
}
def unappliedDb(using qctx: Quotes)(typeRepr: qctx.reflect.TypeRepr): Map[NameReference, Set[NameReference]] = {
InheritanceDbInspector.make(qctx).makeUnappliedInheritanceDb(typeRepr)
}
def fullDb(using qctx: Quotes)(typeRepr: qctx.reflect.TypeRepr): Map[AbstractReference, Set[AbstractReference]] = {
FullDbInspector.make(qctx).buildFullDb(typeRepr)
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/macrortti/LTag.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.macrortti
final case class LTag[T <: AnyKind](tag: LightTypeTag)
/**
* these are different summoners for light tags, it's fine for them to be the same structurally
*/
object LTag {
def apply[T <: AnyKind: LTag]: LTag[T] = implicitly
inline implicit def materialize[T <: AnyKind]: LTag[T] = LTag(izumi.reflect.dottyreflection.Inspect.inspectStrong[T])
final case class Weak[T <: AnyKind](tag: LightTypeTag)
object Weak {
def apply[T <: AnyKind: LTag.Weak]: LTag.Weak[T] = implicitly
inline implicit def materialize[T <: AnyKind]: LTag.Weak[T] = LTag.Weak(izumi.reflect.dottyreflection.Inspect.inspect[T])
}
type StrongHK[T <: AnyKind] = LTag[T]
type WeakHK[T <: AnyKind] = LTag.Weak[T]
}
================================================
FILE: izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/macrortti/package.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect
package object macrortti {
type LWeakTag[T <: AnyKind] = LTag.Weak[T]
object LWeakTag {
def apply[T: LWeakTag]: LWeakTag[T] = implicitly
}
type LTagK[K[_]] = LTag[K]
type LTagKK[K[_, _]] = LTag[K]
type LTagK3[K[_, _, _]] = LTag[K]
type LTagT[K[_[_]]] = LTag[K]
type LTagTK[K[_[_], _]] = LTag[K]
type LTagTKK[K[_[_], _, _]] = LTag[K]
type LTagTK3[K[_[_], _, _, _]] = LTag[K]
object LTagK {
/**
* Construct a type tag for a higher-kinded type `K[_]`
*
* Example:
* {{{
* LTagK[Option]
* }}}
*/
def apply[K[_]: LTagK]: LTagK[K] = implicitly
}
object LTagKK {
def apply[K[_, _]: LTagKK]: LTagKK[K] = implicitly
}
object LTagK3 {
def apply[K[_, _, _]: LTagK3]: LTagK3[K] = implicitly
}
object LTagT {
def apply[K[_[_]]: LTagT]: LTagT[K] = implicitly
}
object LTagTK {
def apply[K[_[_], _]: LTagTK]: LTagTK[K] = implicitly
}
object LTagTKK {
def apply[K[_[_], _, _]: LTagTKK]: LTagTKK[K] = implicitly
}
object LTagTK3 {
def apply[K[_[_], _, _, _]: LTagTK3]: LTagTK3[K] = implicitly
}
// simple materializers
inline def LTT[T]: LightTypeTag = dottyreflection.Inspect.inspect[T]
inline def `LTT[_]`[T[_]]: LightTypeTag = dottyreflection.Inspect.inspect[T]
inline def `LTT[+_]`[T[+_]]: LightTypeTag = dottyreflection.Inspect.inspect[T]
inline def `LTT[A,B,_>:B<:A]`[A, B <: A, T[_ >: B <: A]]: LightTypeTag = dottyreflection.Inspect.inspect[T]
inline def `LTT[A[_],_[_[x] <: A[x]]`[A[_], T[B[x] <: A[x]]]: LightTypeTag = dottyreflection.Inspect.inspect[T]
inline def `LTT[_[_]]`[T[_[_]]]: LightTypeTag = dottyreflection.Inspect.inspect[T]
inline def `LTT[_[+_]]`[T[_[+_]]]: LightTypeTag = dottyreflection.Inspect.inspect[T]
inline def `LTT[_[_[_]]]`[T[_[_[_]]]]: LightTypeTag = dottyreflection.Inspect.inspect[T]
// Note that this variation is not required on Scala 2.13.5, a mismatch between versions on variance handling currently
inline def `LTT[_[+_[_]]]`[T[_[+_[_]]]]: LightTypeTag = dottyreflection.Inspect.inspect[T]
inline def `LTT[_,_]`[T[_, _]]: LightTypeTag = dottyreflection.Inspect.inspect[T]
inline def `LTT[_[_,_]]`[T[_[_, _]]]: LightTypeTag = dottyreflection.Inspect.inspect[T]
inline def `LTT[_[+_,+_]]`[T[_[+_, +_]]]: LightTypeTag = dottyreflection.Inspect.inspect[T]
inline def `LTT[_[_,_],_,_]`[T[_[_, _], _, _]]: LightTypeTag = dottyreflection.Inspect.inspect[T]
inline def `LTT[_[+_,+_],_,_]`[T[_[+_, +_], _, _]]: LightTypeTag = dottyreflection.Inspect.inspect[T]
inline def `LTT[_[_],_[_]]`[T[_[_], _[_]]]: LightTypeTag = dottyreflection.Inspect.inspect[T]
inline def `LTT[_[_[_],_[_]]]`[T[_[_[_], _[_]]]]: LightTypeTag = dottyreflection.Inspect.inspect[T]
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/internal/fundamentals/collections/IzCollectionsTest.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.internal.fundamentals.collections
import izumi.reflect.internal.fundamentals.collections.IzCollections._
import org.scalatest.wordspec.AnyWordSpec
import scala.collection.mutable
class IzCollectionsTest extends AnyWordSpec {
"Collection utils" should {
"allow to convert mappings to multimaps" in {
assert(List("a" -> 1, "a" -> 2).toMultimap == Map("a" -> Set(1, 2)))
assert(List("a" -> 1, "a" -> 2).toMutableMultimap == mutable.Map("a" -> mutable.Set(1, 2)))
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/internal/fundamentals/platform/IzStringTest.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.internal.fundamentals.platform
import izumi.reflect.internal.fundamentals.platform.strings.IzString._
import org.scalatest.wordspec.AnyWordSpec
class IzStringTest extends AnyWordSpec {
"Extended string" should {
"support boolean parsing" in {
assert("false".asBoolean().contains(false))
assert("true".asBoolean().contains(true))
assert(null.asInstanceOf[String].asBoolean().isEmpty)
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/CurrentDottySupportExtentTest.scala
================================================
package izumi.reflect.test
import izumi.reflect.macrortti._
trait CurrentDottySupportExtentTest extends TagAssertions {
trait Y
trait Z extends Y
trait XS[+K]
trait X[+AAAAAAARG, -B0] extends XS[AAAAAAARG] {}
trait A
trait B extends A
trait Listoid[+K]
trait Traitoid
trait Invariantoid[V]
trait SubInvariantoid[V] extends Invariantoid[V] with Traitoid
"super-basic test 1" in { test() }
// FIXME workaround for 'Cyclic reference involving class Bar'
def test() = {
val intTag = LTT[Int]
class Foo[+F[_]]()
class Bar[+F[+_, -_]]() extends Foo[F[Int, *]]
class Baz() extends X[String, Z]
val bazTag = LTT[Baz]
val bazTag2 = LTT[Baz]
val barXTag = LTT[Bar[X]]
assertSame(bazTag, bazTag2)
assertDifferent(bazTag, barXTag)
assertChild(bazTag, bazTag2)
assertNotChild(bazTag, barXTag)
assertChild(LTT[B], LTT[A])
val listTag = `LTT[_]`[Listoid]
val listIntTag = LTT[Listoid[Int]]
// see https://github.com/7mind/izumi/pull/1528
assertSame(bazTag.combine(), bazTag2)
assertChild(listTag.combine(intTag), listIntTag)
val listTag0 = `LTT[_]`[List]
val listIntTag0 = LTT[List[Int]]
assertChild(listTag0.combine(intTag), listIntTag0)
val invTag0 = `LTT[_]`[SubInvariantoid]
val _ = LTT[Invariantoid[Int]]
val combined = invTag0.combine(intTag)
assertChild(combined, LTT[Traitoid])
val tupleTag0 = LTT[(Any, Any)]
val tupleTag1 = LTT[(Baz, Baz)]
val tupleTag2 = LTT[(AnyVal, AnyVal)]
val tupleTag3 = LTT[(Double, Double)]
assertChild(tupleTag1, tupleTag0)
assertChild(tupleTag3, tupleTag2)
assertChild(LTT[List[B]], LTT[Seq[A]])
assertChild(`LTT[_]`[List].combine(LTT[B]), `LTT[_]`[Seq].combine(LTT[A]))
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/InheritedModel.scala
================================================
package izumi.reflect.test
trait InheritedModel {
class Dep
trait BIOService[F[_, _]]
type BIOServiceL[F[+_, +_], E, A] = BIOService[λ[(X, Y) => F[A, E]]]
type F2To3[F[_, _], R, E, A] = F[E, A]
type EitherR[-_, +L, +R] = Either[L, R]
type EitherRSwap[-_, +L, +R] = Either[R, L]
trait BlockingIO3[F[_, _, _]]
type BlockingIO[F[_, _]] = BlockingIO3[λ[(R, E, A) => F[E, A]]]
type IntersectionBlockingIO[F[_, _], G[_, _]] = BlockingIO3[λ[(R, E, A) => F[E, A] with G[A, E]]]
type RepeatedBlockingIO[F[_, _]] = BlockingIO3[λ[(R, E, A) => F[A, A]]]
type RepeatedNonLambdaBlockingIO[F[_, _]] = BlockingIO3[λ[(R, E, A) => F[Int, Int]]]
type RepeatedSymbolNonLambdaBlockingIO[F[_, _]] = BlockingIO3[λ[(R, E, A) => F[Either[Int, Int], Either[Int, String]]]]
trait BlockingIO3T[F[_, _[_], _]]
type BlockingIOT[F[_[_], _]] = BlockingIO3T[({ type l[R, E[_], A] = F[E, A] })#l]
type LambdaParamCtorBlockingIOT[A] = BlockingIO3T[({ type l[R, F[_], X] = F[A] })#l]
type Swap[A, B] = Either[B, A]
type SwapF2[F[_, _], A, B] = F[B, A]
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/LTTRenderablesTest.scala
================================================
package izumi.reflect.test
import izumi.reflect.macrortti._
class LTTRenderablesTest extends TagAssertions {
object X {
object Y {
class C
}
}
"LTT renderables" should {
"render simple lambdas using placeholders when using scalaStyledRepr" in {
val list = `LTT[_]`[List].scalaStyledRepr
val either = `LTT[_,_]`[Either].scalaStyledRepr
val either2 = `LTT[_]`[Either[Int, *]].scalaStyledRepr
val either3 = `LTT[_]`[Either[*, Int]].scalaStyledRepr
val optionT = `LTT[_]`[OptionT[List, *]].scalaStyledRepr
assert(list == "scala.collection.immutable.List[+_]")
assert(either == "scala.util.Either[+_,+_]")
assert(either2 == "scala.util.Either[+scala.Int,+_]")
assert(either3 == "scala.util.Either[+_,+scala.Int]")
assert(optionT == "izumi.reflect.test.OptionT[scala.collection.immutable.List[+_],_]")
}
"render complex lambdas using long form when using scalaStyledRepr" in {
type Const[+A, +B] = B
type SwapEither[+A, +B] = Either[B, A]
type SwapOptionT[A, B[_]] = OptionT[B, A]
type UseInner[A] = OptionT[Either[A, *], A]
type UseInner2[A, B] = OptionT[Either[A, *], B]
type Reuse[A] = Either[A, A]
val identity = `LTT[_]`[ID.Identity].scalaStyledRepr
val const = `LTT[_,_]`[Const].scalaStyledRepr
val swapEither = `LTT[_,_]`[SwapEither].scalaStyledRepr
val swapOptionT = `LTT[_]`[SwapOptionT[*, ID.Identity]].scalaStyledRepr
val useInner = `LTT[_]`[UseInner].scalaStyledRepr
val useInner2 = `LTT[_,_]`[UseInner2].scalaStyledRepr
val reuse = `LTT[_]`[Reuse].scalaStyledRepr
assert(identity == "[A] ➾ A")
assert(const == "[A,B] ➾ B")
assert(swapEither == "[A,B] ➾ scala.util.Either[+B,+A]")
val expectedDepth = if (IsScala3) 1 else 2
assert(swapOptionT == s"izumi.reflect.test.OptionT[[A$expectedDepth] ➾ A$expectedDepth,_]")
assert(useInner == s"[A] ➾ izumi.reflect.test.OptionT[[A$expectedDepth] ➾ scala.util.Either[+A,+A$expectedDepth],A]")
assert(useInner2 == s"[A,B] ➾ izumi.reflect.test.OptionT[[A$expectedDepth] ➾ scala.util.Either[+A,+A$expectedDepth],B]")
assert(reuse == "[A] ➾ scala.util.Either[+A,+A]")
}
"types in nested objects should be rendered with dot separator" in {
assert(LTT[X.Y.C].scalaStyledRepr == "izumi.reflect.test.LTTRenderablesTest.X.Y.C")
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/SharedLightTypeTagProgressionTest.scala
================================================
package izumi.reflect.test
import izumi.reflect.macrortti._
/**
* The tests here are *progression* tests, that means they test that something *doesn't work*
*
* If a test here starts to fail that's a GOOD thing - that means a new feature is now supported.
* When that happens you can remove the `broken` condition inversions and move the test to
* the non-progression test suite.
*
* All tests must have `broken` clauses wrapping the expected GOOD conditions if a feature
* were to work. If a test is missing `broken` clause, it's a probably not a progression test
* anymore and should be moved.
*/
abstract class SharedLightTypeTagProgressionTest extends TagAssertions with TagProgressions {
"[progression] lightweight type tags (all versions)" should {
import TestModel._
"progression test: can't support subtyping of type prefixes" in {
val a = new C {}
broken {
assertChild(LTT[a.A], LTT[C#A])
}
assertDifferent(LTT[a.A], LTT[C#A])
assertNotChild(LTT[C#A], LTT[a.A])
}
"progression test: can't support subtyping of concrete type projections" in {
trait A {
trait T
}
trait B extends A
val tagA = LTT[A#T]
val tagB = LTT[B#T]
assertSame(LTT[A#T], LTT[A#T])
assertDifferent(LTT[B#T], LTT[A#T])
broken {
assertChild(tagB, tagA)
}
}
"progression test: bounds-based subtype checks for lambdas do not work properly (LambdaParameter must contain bounds and NameReferences shouldn't for this to work)" in {
// I consider this stuff practically useless
type X[A >: H4 <: H2] = Set[A]
type X1[A >: H3 <: H3] = Set[A]
type X2[A >: H5 <: H5] = Set[A]
// def compare[a >: c <: b, b <: d, c <: d, d, A[x >: a <: b] <: B[x], B[_ >: c <: d], x >: a <: b](s: A[x], t: B[_ >: c <: d]) = null
broken {
//// compare[H5, H5, H4, H2, X2, X, H5](null: Set[H5], null) // error
//// (null: Set[H5]): Set[_ >: H4 <: H2] // error
assertNotChild(`LTT[A,B,_>:B<:A]`[H5, H5, X2], `LTT[A,B,_>:B<:A]`[H2, H4, X])
}
// compare[H3, H3, H4, H2, X1, X, H3](null: Set[H3], null)
// (null: Set[H3]): Set[_ >: H4 <: H2]
assertChild(`LTT[A,B,_>:B<:A]`[H3, H3, X1], `LTT[A,B,_>:B<:A]`[H2, H4, X])
}
"progression test: indirect structural checks do not work" in {
assertDifferent(LTT[{ type A }], LTT[Object])
broken {
assertChildStrict(LTT[C], LTT[{ type A }])
}
}
"progression test: combined intersection lambda tags still contain some junk bases (coming from the unsound same-arity assumption in LightTypeTag#combine)" in {
val tCtor = `LTT[_,_]`[T3]
val combined = tCtor.combine(LTT[Int], LTT[Boolean])
val debugCombined = combined.debug("combined")
val alias = LTT[T3[Int, Boolean]]
val direct = LTT[W1 with W4[Boolean] with W5[Int]]
broken {
assert(!debugCombined.contains("W4[=scala.Int]"))
}
broken {
assert(!debugCombined.contains("W3[=scala.Int]"))
}
broken {
assertDebugSame(combined, alias)
}
broken {
assertDebugSame(combined, direct)
}
}
"progression test: combined lambda tags still contain some junk bases (coming from the unsound same-arity assumption in LightTypeTag#combine)" in {
val curriedApplied = `LTT[_,_]`[Right].combine(LTT[Throwable]).combine(LTT[Unit])
val debug1 = curriedApplied.debug()
assertSame(curriedApplied, LTT[Right[Throwable, Unit]])
assert(debug1.contains(": scala.util.Right[+java.lang.Throwable,+scala.Unit]"))
assert(debug1.contains("- scala.util.Right[+java.lang.Throwable,+scala.Unit]"))
assert(debug1.contains("* scala.Product"))
assert(debug1.contains("- λ %1 → scala.util.Right[+java.lang.Throwable,+1]"))
assert(debug1.contains("- λ %0,%1 → scala.util.Right[+0,+1]"))
broken {
assert(!debug1.contains("λ %1 → scala.util.Right[+scala.Unit,+1]"))
}
}
"progression test: Dotty fails to `support methods with type parameters in structural refinements`" in {
trait X { def x[A](a: A): A }
assertNotChildStrict(LTT[X { def x[A](a: A): Boolean }], LTT[X { def x[A](a: A): Int }])
assertChildStrict(LTT[X { def x[A](a: A): Boolean }], LTT[X { def x[A](a: A): AnyVal }])
broken {
assertChildStrict(LTT[X { def x[A](a: A): Boolean }], LTT[X { def x[A](a: A): A }])
}
}
"fails to treat tautological refinements as equal to the underlying type" in {
trait X { def x[A](a: A): A }
broken {
assertSameStrict(LTT[X], LTT[X { def x[A](a: A): A }])
assertSameStrict(LTT[Object], LTT[Object { def equals(obj: Object): Boolean }])
}
}
"fails to support methods with multiple parameter lists in refinements" in {
brokenOnScala2 {
assertNotChildStrict(LTT[{ def x(i: Int)(b: Boolean): Int }], LTT[{ def x(b: Boolean): Int }])
}
brokenOnScala2 {
assertNotChildStrict(LTT[{ def x(i: Int)(b: Boolean): Int }], LTT[{ def x(i: Int): Int }])
}
brokenOnScala3 {
// no distinction between method with multiple param lists and one param list
assertNotChildStrict(LTT[{ def x(i: Int)(b: Boolean): Int }], LTT[{ def x(i: Int, b: Boolean): Int }])
}
assertChildStrict(LTT[{ def x(i: Int)(b: Boolean): Int }], LTT[{ def x(i: Int)(b: Boolean): Any }])
// This isn't quite true according to Scalac:
// implicitly[({ def x(i: Int)(b: Boolean): Int }) <:< ({ def x(i: Int)(b: Nothing): Int })] // false
// But since the equivalent with values instead of methods is true, we regard this as true:
// implicitly[({ def x(i: Int): Boolean => Int }) <:< ({ def x(i: Int): Nothing => Int })] // true
assertChildStrict(LTT[{ def x(i: Int)(b: Boolean): Int }], LTT[{ def x(i: Int)(b: Nothing): Int }])
}
"progression test: fails to `Any/Object relation is consistent with Scala`" in {
implicitly[Object <:< Any]
def isNoImplicit[A <: AnyRef](implicit ev: A = null): Boolean = ev eq null
assert(isNoImplicit[Any <:< Object])
assertChild(LTT[Object], LTT[Any])
broken {
assertNotChild(LTT[Any], LTT[Object])
}
assertDifferent(LTT[Any], LTT[Object])
}
"progression test: can't distinguish between equal-bounded type and alias inside refinements on dotty" in {
val t4 = LTT[{ type X >: Any <: Any }] // (java.lang.Object {type X = Any}), but should be (java.lang.Object {type X::})
val t5 = LTT[{ type X = Any }]
brokenOnScala3 {
assertDifferent(t4, t5)
}
}
"progression test: Null type is a subtype of Nothing, but shouldn't be" in {
broken {
assertNotChild(LTT[Null], LTT[Nothing])
}
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/SharedLightTypeTagTest.scala
================================================
package izumi.reflect.test
import izumi.reflect.macrortti.LightTypeTagRef.{AbstractReference, AppliedNamedReference, Boundaries, FullReference, NameReference, TypeParam, Variance}
import izumi.reflect.macrortti._
import scala.collection.immutable.ListSet
import scala.collection.{BitSet, immutable, mutable}
abstract class SharedLightTypeTagTest extends TagAssertions {
import TestModel._
"lightweight type tags (all versions)" should {
"support distinction between subtypes" in {
val str = LTT[String]
val subStrA = LTT[SubStrA]
val subStrB = LTT[SubStrB]
val subStrC = LTT[SubStrC]
val subStrD = LTT[SubStrD]
val subSubStr = LTT[SubSubStr]
assertChildStrict(subStrA, str)
assertChildStrict(subSubStr, str)
assertChildStrict(subSubStr, subStrA)
assertNotChildStrict(subSubStr, subStrB)
assertSameStrict(subStrC, str)
assertNotChild(subStrA, subStrB)
assertSame(subStrA, subStrD)
// see https://github.com/7mind/izumi/pull/1528
assertSame(str.combine(), str)
val strInhBases = LightTypeTagUnpacker(str).inheritance(str.ref.asInstanceOf[NameReference])
val subStrAInhBases = LightTypeTagUnpacker(subStrA).inheritance(subStrA.ref.asInstanceOf[NameReference].copy(boundaries = Boundaries.Empty))
val subSubStrInhBases = LightTypeTagUnpacker(subSubStr).inheritance(subSubStr.ref.asInstanceOf[NameReference].copy(boundaries = Boundaries.Empty))
assert(subStrAInhBases == (strInhBases ++ Set(str.ref.asInstanceOf[NameReference])))
assert(subSubStrInhBases == (strInhBases ++ Set(str.ref.asInstanceOf[NameReference])))
val strFullBases = LightTypeTagUnpacker(str).bases(str.ref.asInstanceOf[NameReference])
val subStrAFullBases = LightTypeTagUnpacker(subStrA).bases(subStrA.ref.asInstanceOf[NameReference])
val subSubStrFullBases = LightTypeTagUnpacker(subSubStr).bases(subSubStr.ref.asInstanceOf[NameReference])
assert(LightTypeTagUnpacker(str).bases.keySet == Set(str.ref.asInstanceOf[AppliedNamedReference]))
assert(subStrA.repr == "izumi.reflect.test.TestModel::SubStrA|")
assert(subStrA.repr != "izumi.reflect.test.TestModel$::SubStrA|")
assert(subStrAFullBases == (strFullBases ++ Set(str.ref.asInstanceOf[AbstractReference])))
assert(subSubStrFullBases == (strFullBases ++ Set(str.ref.asInstanceOf[AbstractReference])))
val foo = LTT[SubStrA \/ Int]
val bar = `LTT[_,_]`[\/].combine(subStrA, LTT[Int])
assertSameStrict(foo, bar)
}
"eradicate intersection tautologies with Any/Object" in {
assertSameStrict(LTT[Any with Option[String]], LTT[Option[String]])
assertSameStrict(LTT[AnyRef with Option[String]], LTT[Option[String]])
assertSameStrict(LTT[Object with Option[String]], LTT[Option[String]])
}
"do not eradicate intersections with Nothing" in {
assertDifferent(LTT[Nothing with Option[String]], LTT[Option[String]])
assertSameStrict(LTT[Nothing with Option[String]], LTT[Option[String] with Nothing])
}
"eradicate self-intersection (X with X)" in {
assertSameStrict(`LTT`[String with String], `LTT`[String])
}
"support subtype checks" in {
assertChild(LTT[Int], LTT[AnyVal])
assertChild(LTT[Int], LTT[Int])
assertChild(LTT[List[Int]], LTT[List[Int]])
assertChild(LTT[List[I2]], LTT[List[I1]])
assertChild(LTT[Either[Nothing, Int]], LTT[Either[Throwable, Int]])
assertChild(LTT[F2[I2]], LTT[F1[I1]])
assertChild(LTT[FT2[IT2]], LTT[FT1[IT2]])
assertChild(LTT[FM2[I2]], LTT[FM1[I1, Unit]])
assertChild(LTT[Option[Nothing]], LTT[Option[Int]])
assertChild(`LTT[_[_],_[_]]`[P1].combine(`LTT[_]`[X1], `LTT[_]`[X2]), LTT[P0[X2, X1]])
assertNotChild(`LTT[_[_],_[_]]`[P1].combine(`LTT[_]`[X1], `LTT[_]`[X2]), LTT[P0[X1, X2]])
assertChild(`LTT[_[_]]`[XP1].combine(`LTT[_]`[X1]), LTT[P0[X2, X1]])
assertChild(`LTT[_[_]]`[XP1].combine(`LTT[_]`[X2]), LTT[P0[X2, X2]])
assertNotChild(`LTT[_[_]]`[XP1].combine(`LTT[_]`[X2]), LTT[P0[X2, X1]])
assertNotChild(`LTT[_[_]]`[XP1].combine(`LTT[_]`[X2]), LTT[P0[X1, X2]])
}
"support unsound subtype checks" in {
// UNSOUND-LAMBDA-COMPARISON
// it's hard to say how should we compare propers with lambdas...
// these two may be inverted by uncommenting corresponding lines in inheritance checks
assertNotChild(LTT[FM2[I2]], `LTT[_,_]`[FM1])
assertNotChild(LTT[List[Int]], `LTT[_]`[List])
// this one should be always false
assertNotChild(LTT[Set[Int]], `LTT[_]`[Set])
}
"support swapped parents" in {
trait KT1[+A1, +B1]
trait KT2[+A2, +B2] extends KT1[B2, A2]
assertChild(LTT[KT2[H1, I1]], LTT[KT1[I1, H1]])
assertNotChild(LTT[KT2[H1, I1]], LTT[KT1[H1, I1]])
assertChild(LTT[KT2[H2, I2]], LTT[KT1[I1, H1]])
assertNotChild(LTT[KT2[H2, I2]], LTT[KT1[H1, I1]])
trait KK1[+A, +B, +U]
trait KK2[+A, +B] extends KK1[B, A, Unit]
assertChild(LTT[KK2[Int, String]], LTT[KK1[String, Int, Unit]])
assertChild(LTT[KK2[H2, I2]], LTT[KK1[I1, H1, Unit]])
assertNotChild(LTT[KK2[H2, I2]], LTT[KK1[H1, I1, Unit]])
assertNotChild(`LTT[_]`[KK2[H2, *]], `LTT[_]`[KK1[H1, *, Unit]])
type KK1Unit[+A, +B] = KK1[A, B, Unit]
type SwappedKK2[+A, +B] = KK2[B, A]
// before, we had incorrect fullBases parent lambdaification on Scala 3:
// fullBases on Scala 3 has 1 type parameter before extractLambdaBase:
// - λ %0 → izumi.reflect.test.SharedLightTypeTagProgressionTest._$KK2[+izumi.reflect.test.TestModel::H2,+0] ->
// * λ %0 → izumi.reflect.test.SharedLightTypeTagProgressionTest._$KK1[+0,+izumi.reflect.test.TestModel::H2,+scala.Unit]
// While should be 2 type parameters:
// - λ %0,%1 → izumi.reflect.test.SharedLightTypeTagProgressionTest.KK2[+0,+1] ->
// * λ %0,%1 → izumi.reflect.test.SharedLightTypeTagProgressionTest.KK1[+1,+0,+scala.Unit]
// Or... maybe it's Scala 3 that's more correct. See SwappedKK2 parents on Scala 2:
// - λ %0,%1 → izumi.reflect.test.SharedLightTypeTagProgressionTest.KK2[+0,+1] ->
// * λ %0,%1 → izumi.reflect.test.SharedLightTypeTagProgressionTest.KK1[+1,+0,+scala.Unit]
// VS SwappedKK2 parents on Scala 3, which is correct (it preserves the swappiness):
// - λ %0,%1 → izumi.reflect.test.SharedLightTypeTagProgressionTest._$KK2[+1,+0] ->
// * λ %0,%1 → izumi.reflect.test.SharedLightTypeTagProgressionTest._$KK1[+0,+1,+scala.Unit]
// Ok... we just supported both forms in LightTypeTagInheritance in 3.0.4... somehow... ... ...
val tag = `LTT[_]`[KK2[H2, *]]
val tag1 = `LTT[_,_]`[SwappedKK2]
// withDebugOutput {
assertChild(tag, `LTT[_]`[KK1[*, H1, Unit]])
assertChild(tag1, `LTT[_,_]`[KK1Unit])
assertNotChild(tag1, `LTT[_,_]`[KK2])
// }
}
"support subtyping of parents parameterized with type lambdas" in {
assertChild(LTT[RoleChild[Either]], LTT[RoleParent[Either[Throwable, *]]])
}
"support subtyping of parents parameterized with type lambdas in combined tags" in {
val childBase = `LTT[_[_,_]]`[RoleChild]
val childArg = `LTT[_,_]`[Either]
val combinedTag = childBase.combine(childArg)
assertSame(combinedTag, LTT[RoleChild[Either]])
}
"support subtyping of parents parameterized with type lambdas in combined tags with multiple parameters" in {
val childBase = `LTT[_[+_,+_],_,_]`[RoleChild2]
val childArgs = Seq(`LTT[_,_]`[Either], LTT[Int], LTT[String])
val combinedTag = childBase.combine(childArgs: _*)
val expectedTag = LTT[RoleParent[Either[Throwable, *]]]
val noncombinedTag = LTT[RoleChild2[Either, Int, String]]
assertSame(combinedTag, noncombinedTag)
assertChild(noncombinedTag, expectedTag)
}
"support PDTs" in {
val a = new C {
override type A = Int
}
val a0 = new C {}
val a1: C = new C {
override type A = Int
}
val a2 = new C {
override type A = String
}
assertSame(LTT[a.A], LTT[Int])
assertDifferent(LTT[a0.A], LTT[Int])
assertDifferent(LTT[a1.A], LTT[Int])
assertDifferent(LTT[a1.A], LTT[a2.A])
assertChild(LTT[a1.A], LTT[a1.A])
assertSame(LTT[a2.A], LTT[String])
}
"intersections are associative" in {
type F1 = (W3[Int] with W1) with I1
type F2 = W3[Int] with (W1 with I1)
type T1[A] = (W3[A] with W1) with I1
type T2[A] = W3[A] with (W1 with I1)
assertSameStrict(LTT[F1], LTT[F2])
assertSameStrict(`LTT[_]`[T1], `LTT[_]`[T2])
}
"runtime-combined intersections are associative" in {
type F1 = W3[Int] with W1
type F11 = (W3[Int] with W1) with I1
type F12 = W3[Int] with (W1 with I1)
type T1[A] = W3[Int] with (W1 with A)
type T2[A] = (W3[Int] with W1) with A
assertIntersection(List(LTT[F1], LTT[I1]), LTT[F11])
assertIntersection(List(LTT[F1], LTT[I1]), LTT[F12])
assertCombine(`LTT[_]`[T1], LTT[I1], LTT[F11])
assertCombine(`LTT[_]`[T1], LTT[I1], LTT[F12])
assertCombine(`LTT[_]`[T2], LTT[I1], LTT[F11])
assertCombine(`LTT[_]`[T2], LTT[I1], LTT[F12])
}
"support type alias and refinement subtype checks" in {
val t1 = LTT[XS]
val t2 = LTT[WithX]
val t3 = LTT[{ type X >: Nothing <: Any }]
val t4 = LTT[{ type X >: Any <: Any }]
val t5 = LTT[{ type X = Any }]
val t6 = LTT[{ type X = Int }]
val t7 = LTT[{ type X <: AnyVal }]
assertChildStrict(t1, t2)
assertChildStrict(t1, t3)
assertNotChild(t3, t4)
assertChildStrict(t4, t3)
assertChildStrict(t5, t3)
assertSameStrict(t3, LTT[{ type X }])
assertNotChild(t3, t7)
assertNotChild(t3, t5)
assertNotChild(t2, t5)
assertNotChild(t3, t4)
assertNotChild(t2, t4)
assertNotChild(t1, t4)
assertChildStrict(t6, t3)
assertChildStrict(t6, t7)
assertNotChildStrict(t6, t4)
assertNotChildStrict(t6, t5)
}
"support refinement higher-kinded subtype checks" in {
val t1 = LTT[{ type F[A] = A }]
val t2 = LTT[{ type F[A] <: Any }]
val t3 = LTT[{ type F[A] <: AnyVal }]
val t4 = LTT[FXS]
assertChildStrict(t1, t2)
assertNotChildStrict(t1, t3)
assertChildStrict(t4, t1)
assertChildStrict(t4, t2)
assertNotChildStrict(t4, t3)
}
"support literal types" in {
assertSame(literalLtt("str2"), literalLtt("str2"))
assertDifferent(literalLtt("str1"), literalLtt("str2"))
assertChildStrict(literalLtt("str"), LTT[String])
assertNotChild(literalLtt("str"), LTT[Int])
val tag = literalLtt("str")
assertRepr(tag, "\"str\"")
}
"resolve comparisons of object and trait with the same name" in {
assertNotChild(LTT[YieldOpCounts.type], LTT[RoleChild[Either]])
assertChild(LTT[YieldOpCounts.type], LTT[YieldOpCounts])
assertDifferent(LTT[YieldOpCounts.type], LTT[YieldOpCounts])
assertNotChild(LTT[YieldOpCounts], LTT[YieldOpCounts.type])
}
"resolve prefixes of annotated types" in {
assertSame(LTT[TPrefix.T @unchecked], LTT[TPrefix.T])
}
"`withoutArgs` comparison works" in {
assert(LTT[Set[Int]].ref.withoutArgs == LTT[Set[Any]].ref.withoutArgs)
assert(LTT[Either[Int, String]].ref.withoutArgs == LTT[Either[Boolean, List[Int]]].ref.withoutArgs)
assertSame(LTT[Set[Int]].withoutArgs, LTT[Set[Any]].withoutArgs)
assertChild(LTT[mutable.LinkedHashSet[Int]].withoutArgs, LTT[collection.Set[Any]].withoutArgs)
assertChild(LTT[ListSet[Int]].withoutArgs, LTT[collection.Set[Any]].withoutArgs)
assertChild(LTT[ListSet[Int]].withoutArgs, LTT[immutable.Set[Any]].withoutArgs)
assertChild(LTT[BitSet].withoutArgs, LTT[collection.Set[Any]].withoutArgs)
}
"`typeArgs` works" in {
assertSame(LTT[Set[Int]].typeArgs.head, LTT[collection.Set[Int]].typeArgs.head)
assertChild(LTT[Set[Int]].typeArgs.head, LTT[collection.Set[AnyVal]].typeArgs.head)
assert(`LTT[_,_]`[Either].typeArgs.isEmpty)
assert(`LTT[_]`[Either[String, *]].typeArgs == List(LTT[String]))
val tag = `LTT[_[_]]`[({ type l[F[_]] = T0[List, F] })#l]
val tagApplied = tag.combine(`LTT[_]`[Option])
assertSame(tagApplied, LTT[T0[List, Option]])
assert(tag.typeArgs == List(`LTT[_]`[List]))
assert(tagApplied.typeArgs == List(`LTT[_]`[List], `LTT[_]`[Option]))
}
"support subtyping of a simple combined type" in {
val ctor = `LTT[_[_]]`[ApplePaymentProvider]
val arg = `LTT[_]`[Id]
val combined = ctor.combine(arg)
assertChild(combined, LTT[H1])
}
"issue #762: properly strip away annotated types / empty refinements / type aliases" in {
val predefString = LTT[String]
val javaLangString = LTT[java.lang.String]
val weirdPredefString = LTT[(scala.Predef.String {}) @IdAnnotation("abc")]
assertSame(predefString, javaLangString)
assertSame(predefString, weirdPredefString)
assertSame(javaLangString, weirdPredefString)
assertDebugSame(predefString, javaLangString)
assertDebugSame(predefString, weirdPredefString)
assertDebugSame(javaLangString, weirdPredefString)
val javaLangOpt = LTT[Option[java.lang.String]]
val predefOpt = LTT[Option[scala.Predef.String]]
val weirdPredefOpt = LTT[Option[(scala.Predef.String {}) @IdAnnotation("x")]]
assertDebugSame(javaLangOpt, predefOpt)
assertDebugSame(javaLangOpt, weirdPredefOpt)
}
"calculate identical hashCode in parsed and constructed instances" in {
val tag1 = LTT[String]
val tag2 = tag1.withoutArgs
assertSameRef(tag1, tag2)
assertSame(tag1, tag2)
assert(tag1.hashCode() == tag2.hashCode())
assert(tag1.hashCode() != 0)
assert(tag1.ref.hashCode() != 0)
}
"support non-positional typetag combination" in {
assertCombineNonPos(`LTT[_,_]`[Either], Seq(None, Some(LTT[Unit])), `LTT[_]`[Either[*, Unit]])
}
"support additional mixin traits after first trait with a HKT parameter" in {
assertChild(LTT[J[Option]], LTT[J1[Option]])
assertChild(LTT[J[Option]], LTT[J3])
assertChild(LTT[J[Option]], LTT[J2])
assertChild(LTT[J[Option]], LTT[J1[Option] with J2])
assertChild(LTT[J[Option]], LTT[J2 with J3])
assertChild(LTT[J[Option]], LTT[J1[Option] with J2 with J3])
}
"support LTagK* family summoners" in {
val tag = LTagK[List].tag
val tag1 = LTagKK[Either].tag
assertSame(tag, `LTT[_]`[List])
assertSame(tag1, `LTT[_,_]`[Either])
}
"support higher-kinded intersection type equality" in {
type T1[A] = W3[A] with W1
type T2[A] = W4[A] with W2
assertSame(`LTT[_]`[T1], `LTT[_]`[T1])
assertDifferent(`LTT[_]`[T1], `LTT[_]`[T2])
}
"support contravariance" in {
assertChildStrict(LTT[Any => Int], LTT[Int => Int])
}
"support typetag combination" in {
assertCombine(`LTT[_[_]]`[T1], `LTT[_]`[Id], LTT[T1[Id]])
assertCombine(`LTT[_[_]]`[T1], `LTT[_]`[FP], LTT[T1[FP]])
assertCombine(`LTT[_[_]]`[T1], `LTT[_]`[FI], LTT[T1[FI]])
assertCombine(`LTT[_[_]]`[T1], `LTT[_]`[List], LTT[T1[List]])
assertCombine(`LTT[_]`[List], LTT[Int], LTT[List[Int]])
assertCombine(`LTT[_,_]`[Either], LTT[Unit], `LTT[_]`[Either[Unit, *]])
assertCombine(`LTT[_[_[_],_[_]]]`[T2], `LTT[_[_],_[_]]`[T0], LTT[T2[T0]])
type ComplexRef[T] = W1 with T { def a(p: T): T; type M = T }
assertCombine(`LTT[_]`[ComplexRef], LTT[Int], LTT[W1 with Int { def a(p: Int): Int; type M = Int }])
assertCombine(`LTT[_[_]]`[({ type l[K[_]] = T0[Id, K] })#l], `LTT[_]`[FP], LTT[T0[Id, FP]])
}
"tautological intersections with Any/Object are discarded from internal structure" in {
assertSameStrict(LTT[(Object {}) @IdAnnotation("x") with Option[(String with Object) {}]], LTT[Option[String]])
assertSameStrict(LTT[(Any {}) @IdAnnotation("x") with Option[(String with Object) {}]], LTT[Option[String]])
assertSameStrict(LTT[(AnyRef {}) @IdAnnotation("x") with Option[(String with Object) {}]], LTT[Option[String]])
assertDebugSame(LTT[(Object {}) @IdAnnotation("x") with Option[(String with Object) {}]], LTT[Option[String]])
assertDebugSame(LTT[(Any {}) @IdAnnotation("x") with Option[(String with Object) {}]], LTT[Option[String]])
assertDebugSame(LTT[(AnyRef {}) @IdAnnotation("x") with Option[(String with Object) {}]], LTT[Option[String]])
}
"wildcards are supported" in {
assertDifferent(LTT[Set[_]], LTT[Set[Any]])
assertChild(LTT[Set[Int]], LTT[Set[_]])
assertNotChild(LTT[Set[_]], LTT[Set[Int]])
assertChild(LTT[Set[Any]], LTT[Set[_]])
assertNotChild(LTT[Set[_]], LTT[Set[Any]])
assertDifferent(LTT[List[_]], LTT[List[Any]])
assertChild(LTT[List[Int]], LTT[List[_]])
assertNotChild(LTT[List[_]], LTT[List[Int]])
assertChild(LTT[List[Any]], LTT[List[_]])
assertChild(LTT[List[_]], LTT[List[Any]])
assertDifferent(LTT[Int => Int], LTT[_ => Int])
assertChild(LTT[Int => Int], LTT[_ => Int])
assertChild(LTT[_ => Int], LTT[Int => Int]) // incorrect but whatever
}
"wildcards with bounds are supported" in {
assertDifferent(LTT[Option[W1]], LTT[Option[_ <: W1]])
assertDifferent(LTT[Option[H2]], LTT[Option[_ >: H4 <: H2]])
assertDifferent(LTT[Option[Any]], LTT[Option[_ >: H4]])
}
"generate tags for wildcards with type boundaries" in {
assertDifferent(LTT[Option[W1]], LTT[Option[_ <: W1]])
assertChild(LTT[Option[W1]], LTT[Option[_ <: W1]])
assertChild(LTT[Option[W2]], LTT[Option[_ <: W1]])
assertNotChild(LTT[Option[W2]], LTT[Option[_ <: I1]])
assertChild(LTT[Option[_ <: W2]], LTT[Option[W1]])
assertChild(LTT[Option[_ <: W2]], LTT[Option[W2]])
assertNotChild(LTT[Option[_ <: I1]], LTT[Option[W2]])
assertChild(LTT[Option[H3]], LTT[Option[_ >: H4 <: H2]])
assertNotChild(LTT[Option[H1]], LTT[Option[_ >: H4 <: H2]])
assertTypeError("val o: Option[H3] = None: Option[_ >: H4 <: H2]")
assertNotChild(LTT[Option[_ >: H4 <: H2]], LTT[Option[H3]])
assertNotChild(LTT[Option[_ >: H4]], LTT[Option[H3]])
assertNotChild(LTT[Option[_ <: H2]], LTT[Option[H3]])
locally { val o: Option[H1] = None: Option[_ <: H2] }
assertTypeError("val o: Option[H1] = None: Option[_ >: H4]")
assertChild(LTT[Option[_ <: H2]], LTT[Option[H1]])
assertNotChild(LTT[Option[_ >: H4]], LTT[Option[H1]])
assertChild(LTT[Option[_ >: H4 <: H2]], LTT[Option[H1]])
if (!IsScala3) {
assertCompiles("val opt: Option[_ >: H4 <: H2] = None: Option[H5]")
} else {
assertCompiles("val opt: Option[_ >: H4 <: H2] = (None: Option[H5]): Option[H4]")
}
assertChild(LTT[Option[H4]], LTT[Option[_ >: H4 <: H2]])
assertChild(LTT[Option[H2]], LTT[Option[_ >: H4 <: H2]])
// this is counterintuitive but that's how Scala works, it ignores lower boundary in contravariant position
assertChild(LTT[Option[H5]], LTT[Option[_ >: H4 <: H2]])
}
"https://github.com/zio/izumi-reflect/issues/315 regression test 2.1.0-M1: IntegrationCheck[F] should not be related to IntegrationCheck[Identity]" in {
assertNotChildStrict(LTT[IntegrationCheck[Option]], LTT[IntegrationCheck[Id]])
}
"normalize stable PDTs (https://github.com/zio/zio/issues/3390)" in {
val t1 = LTT[PDTNormA.Service]
val t2 = LTT[PDTNormB.Service]
assertSameStrict(t2, t1)
assertDebugSame(t2, t1)
val PDTAlias1 = PDTNormB
val PDTAlias2 = PDTAlias1
val PDTAlias3 = PDTAlias2
val PDTAlias4 = PDTAlias3
val PDTAlias5 = PDTAlias4
val t3 = LTT[PDTAlias5.Service]
assertSameStrict(t3, t1)
assertDebugSame(t3, t1)
object xa {
val PDTAlias6 = PDTAlias5
}
val t4 = LTT[PDTNormA.type]
val t5 = LTT[PDTAlias5.type]
val t6 = LTT[xa.PDTAlias6.type]
assertSameStrict(t5, t4)
assertDebugSame(t5, t4)
assertSameStrict(t6, t4)
assertDebugSame(t6, t4)
val literal = "x"
val aliasLiteral: literal.type = literal
val t7 = LTag[literal.type].tag
val t8 = LTag[aliasLiteral.type].tag
assertSameStrict(t7, t8)
assertDebugSame(t7, t8)
}
"distinguish nested path dependent types (https://github.com/zio/izumi-reflect/issues/363)" in {
trait Base {
object Nested {
trait Member
}
}
object A extends Base
object B extends Base
assertDifferent(LTT[A.Nested.Member], LTT[B.Nested.Member])
}
// Workaround for https://github.com/scala/scala3/issues/23279
def dealiasTestWrapper(): Unit = {
object lifecycle {
object Lifecycle {
trait FromZIO
}
}
object defn {
val Lifecycle: lifecycle.Lifecycle.type = lifecycle.Lifecycle
}
val xa: defn.Lifecycle.type = defn.Lifecycle
assertSameStrict(LTT[xa.FromZIO], LTT[lifecycle.Lifecycle.FromZIO])
}
"dealias nested singletons, regression test for singleton dealias regression introduced in 3.0.0 (https://github.com/zio/izumi-reflect/pull/504)" in {
dealiasTestWrapper()
}
"dealias deeply singleton val aliases" in {
trait diffBase {
object Nested {
trait Member
}
}
object diffA extends diffBase
object diffB {
val Nested: diffA.Nested.type = diffA.Nested
}
object diffNB {
val Nested: diffB.Nested.type = diffB.Nested
}
assertSameStrict(LTT[diffNB.Nested.Member], LTT[diffA.Nested.Member])
}
"properly dealias and assign prefixes to existential types and wildcards" in {
val withNothing = LTT[TestModel.With[Nothing]]
val with_ = LTT[TestModel.With[_]]
assert(withNothing.debug().contains(": izumi.reflect.test.TestModel::With[=scala.Nothing]"))
assert(withNothing.debug().contains("- izumi.reflect.test.TestModel::With[=scala.Nothing]"))
assert(with_.debug().contains(": izumi.reflect.test.TestModel::With[=?]"))
assert(with_.debug().contains("- izumi.reflect.test.TestModel::With[=?]"))
val list1 = LTT[List[_]]
val list2 = LTT[List[_]]
assertChild(LTT[List[Int]], list2)
assertChild(LTT[scala.collection.immutable.List[Int]], list1)
assertChild(list1, list2)
assertChild(list2, list1)
assertDebugSame(list1, list2)
}
"no redundant $ in object names" in {
val ltt = LTT[TestModel.BasicCases.BasicCase2.TestImpl0Good]
assert(!ltt.debug().contains("BasicCase2$"))
assert(!ltt.debug().contains("BasicCases$"))
assert(!ltt.repr.contains("BasicCase2$"))
assert(!ltt.repr.contains("BasicCases$"))
assert(!ltt.toString.contains("BasicCase2$"))
assert(!ltt.toString.contains("BasicCases$"))
assert(ltt.longNameWithPrefix == "izumi.reflect.test.TestModel.BasicCases.BasicCase2.TestImpl0Good")
}
"support basic None.type subtype check" in {
assertChild(LTT[None.type], LTT[Option[Int]])
}
"supports complex type lambdas" in {
assertSame(`LTT[_,_]`[NestedTL[Const, *, *]], `LTT[_,_]`[λ[(A, B) => FM2[(B, A)]]])
assertSame(
`LTT[_[_]]`[({ type l[F[_]] = NestedTL2[W1, W2, F] })#l],
`LTT[_[_]]`[({ type l[G[_]] = FM2[G[S[W2, W1]]] })#l]
)
assertChild(`LTT[_,_]`[NestedTL[Const, *, *]], `LTT[_,_]`[λ[(A, B) => FM2[(B, A)]]])
}
"applied tags should not contain junk bases" in {
val debug0 = LTT[List[Any]].debug()
// val debug0 = PlatformSpecific.fromRuntime[List[Any]].debug()
assert(!debug0.contains("scala.List"))
assert(!debug0.contains("package::List"))
assert(!debug0.contains(""))
assert(!debug0.contains(""))
assert(debug0.contains("- scala.collection.immutable.List[+scala.Any]"))
assert(debug0.contains("- λ %0 → scala.collection.immutable.List[+0]"))
// val debug1 = LTT[List[_]].debug()
val debug1 = PlatformSpecific.fromRuntime[List[_]].debug()
assert(!debug1.contains("scala.List"))
assert(!debug1.contains("package::List"))
assert(!debug1.contains(""))
assert(!debug1.contains(""))
assert(debug1.contains("- scala.collection.immutable.List[+?]"))
assert(debug1.contains("- λ %0 → scala.collection.immutable.List[+0]"))
val debug2 = LTT[Either[RoleChild[IO], Product]].debug()
// val debug2 = PlatformSpecific.fromRuntime[Either[RoleChild[IO], Product]].debug()
assert(!debug2.contains("package::Either"))
assert(!debug2.contains(""))
assert(!debug2.contains(""))
assert(!debug2.contains("TestModel.E"))
assert(!debug2.contains("TestModel.A"))
assert(debug2.contains("- λ %0 → izumi.reflect.test.TestModel::RoleChild[=0]"))
assert(debug2.contains("* λ %0 → izumi.reflect.test.TestModel::RoleParent[=λ %1:0 → 0[=java.lang.Throwable,=1:0]]"))
}
"intersection lambda tags should not contain junk bases" in {
val tCtor = `LTT[_,_]`[T3]
// val tCtor = PlatformSpecific.fromRuntime(scala.reflect.runtime.universe.typeOf[T3[Any, Any]].typeConstructor)
val debugCtor = tCtor.debug("ctor")
val combined = tCtor.combine(LTT[Int], LTT[Boolean])
val debugCombined = combined.debug("combined")
val alias = LTT[T3[Int, Boolean]]
val direct = LTT[W1 with W4[Boolean] with W5[Int]]
assert(!debugCtor.contains(""))
assert(!debugCtor.contains(""))
assert(!debugCtor.contains("- T"))
assert(!debugCtor.contains("W4[=B]"))
assert(!debugCtor.contains("W3[=B]"))
assert(!debugCtor.contains("W5[=A]"))
assert(!direct.debug().contains("W4[=Int]"))
assert(!direct.debug().contains("W4[=scala.Int]"))
assert(!debugCombined.contains(""))
assert(!debugCombined.contains(""))
assert(!debugCombined.contains("- T"))
assert(!debugCombined.contains("W4[=B]"))
assert(!debugCombined.contains("W3[=B]"))
assert(!debugCombined.contains("W5[=A]"))
assert(debugCombined.contains("W5[=scala.Int]"))
assertDebugSame(alias, direct)
}
"lambda tags should not contain junk bases" in {
val debug1 = `LTT[_,_]`[Right].debug()
assert(!debug1.contains("package::Either"))
assert(!debug1.contains("scala.package.A"))
assert(!debug1.contains("scala.package.B"))
assert(debug1.contains("- λ %0,%1 → scala.util.Right[+0,+1]"))
assert(debug1.contains("* scala.Product"))
val debug2 = `LTT[_,_]`[Right].combine(LTT[Int], LTT[Int]).debug()
assert(!debug2.contains("package::Either"))
assert(!debug2.contains("scala.package.A"))
assert(!debug2.contains("scala.package.B"))
assert(debug2.contains("- λ %0,%1 → scala.util.Right[+0,+1]"))
assert(debug2.contains("* scala.Product"))
val debug3 = LTT[RoleParent[Right[Throwable, *]]].debug()
// val debug3 = PlatformSpecific.fromRuntime[RoleParent[Right[Throwable, *]]].debug()
assert(!debug3.contains("package::Right"))
assert(!debug3.contains(""))
assert(!debug3.contains(""))
assert(!debug3.contains("TestModel.E"))
assert(!debug3.contains("TestModel.A"))
assert(!debug3.contains("+scala.Nothing"))
// assert(debug3.contains("- λ %0 → scala.util.Right[+java.lang.Throwable,+0]"))
if (IsScala3) {
assert(debug3.contains("- λ %1:0,%1:1 → scala.util.Right[+1:0,+1:1]"))
} else {
assert(debug3.contains("- λ %0,%1 → scala.util.Right[+0,+1]"))
}
assert(debug3.contains("* scala.Product"))
val debug4 = `LTT[_]`[Right[Throwable, *]].debug()
// val debug4 = PlatformSpecific
// .fromRuntime(
// scala.reflect.runtime.universe.typeOf[{ type l[a] = Right[Throwable, a] }].member(scala.reflect.runtime.universe.TypeName("l")).typeSignature
// ).debug()
assert(!debug4.contains("package::Right"))
assert(!debug4.contains(""))
assert(!debug4.contains(""))
assert(!debug4.contains("TestModel.E"))
assert(!debug4.contains("TestModel.A"))
assert(!debug4.contains("+scala.Nothing"))
// assert(debug4.contains("- λ %0 → scala.util.Right[+java.lang.Throwable,+0]"))
if (IsScala3) {
assert(debug4.contains("- λ %1:0,%1:1 → scala.util.Right[+1:0,+1:1]"))
} else {
assert(debug4.contains("- λ %0,%1 → scala.util.Right[+0,+1]"))
}
assert(debug4.contains("* scala.Product"))
val oneArgApplied = `LTT[_,_]`[Right].combine(LTT[Throwable]).combine(LTT[Unit])
val debug5 = oneArgApplied.debug()
assert(!debug5.contains("package::Right"))
assert(!debug5.contains(""))
assert(!debug5.contains(""))
assert(!debug5.contains("scala.package.A"))
assert(!debug5.contains("scala.package.B"))
assert(!debug5.contains("+scala.Nothing"))
assert(debug5.contains("* scala.Product"))
assert(debug5.contains("- λ %1 → scala.util.Right[+java.lang.Throwable,+1]"))
assert(debug5.contains("- λ %0,%1 → scala.util.Right[+0,+1]"))
}
"No degenerate lambdas (regression test https://github.com/zio/izumi-reflect/issues/345)" in {
val fullDb = LTT[List[Int]].basesdb
fullDb.foreach {
case (_, parents) =>
parents.foreach {
case LightTypeTagRef.Lambda(List(name), FullReference(_, params, _)) =>
assert(params.exists {
case TypeParam(NameReference(ref, _, _), _) =>
name == ref
case _ => false
})
case _ =>
}
}
}
"check subtyping when higher-kinds are involved on Scala 3" in {
assertChild(LTT[FT2[IT2]], LTT[FT1[IT1]])
assertChild(`LTT[_[+_[_]]]`[FT2].combine(`LTT[_[+_]]`[IT2]), LTT[FT1[IT1]])
assertDifferent(`LTT[_[+_[_]]]`[FT2].combine(`LTT[_[+_]]`[IT2]), LTT[FT1[IT1]])
assertChild(`LTT[_[+_[_]]]`[FT2].combine(`LTT[_[+_]]`[IT1]), LTT[FT1[IT1]])
assertDifferent(`LTT[_[+_[_]]]`[FT2].combine(`LTT[_[+_]]`[IT1]), LTT[FT1[IT1]])
assertChild(`LTT[_[+_[_]]]`[FT1].combine(`LTT[_[+_]]`[IT2]), LTT[FT1[IT1]])
assertDifferent(`LTT[_[+_[_]]]`[FT1].combine(`LTT[_[+_]]`[IT2]), LTT[FT1[IT1]])
assertSame(`LTT[_[+_[_]]]`[FT1].combine(`LTT[_[+_]]`[IT1]), LTT[FT1[IT1]])
}
"support higher-kinded intersection type subtyping" in {
type F1 = W3[Int] with W1
type F2 = W4[Int] with W2
type T1[A] = W3[A] with W1
type T2[A] = W4[A] with W2
val f1 = LTT[F1]
val f2 = LTT[F2]
assertChild(f1, LTT[W3[Int]])
assertChild(f1, LTT[W1])
assertChild(f2, f1)
val t1 = `LTT[_]`[T1]
val t2 = `LTT[_]`[T2]
val w3 = `LTT[_]`[W3]
val w4 = `LTT[_]`[W4]
assertChild(t1, w3)
assertChild(t1, LTT[W1])
assertChild(w4, w3)
assertChild(t2, t1)
}
"support higher-kinded intersection type combination" in {
val tCtor = `LTT[_,_]`[T3]
val combined = tCtor.combine(LTT[Int], LTT[Boolean])
val alias = LTT[T3[Int, Boolean]]
val direct = LTT[W1 with W4[Boolean] with W5[Int]]
assertChild(alias, direct)
assertChild(combined, alias)
assertChild(combined, direct)
assertSame(alias, direct)
assertSame(alias, combined)
assertDifferent(combined, LTT[Either[Int, Boolean]])
assertDifferent(combined, LTT[T3[Boolean, Int]])
assertNotChild(combined, LTT[Either[Int, Boolean]])
assertNotChild(combined, LTT[T3[Boolean, Int]])
assertChild(combined, LTT[W5[Int]])
assertChild(combined, LTT[W4[Boolean]])
// withDebugOutput {
assertChild(combined, LTT[W3[Boolean]])
// }
assertChild(combined, LTT[W1])
assertChild(combined, LTT[W2])
assertChild(combined, LTT[W1 with W3[Boolean]])
assertNotChild(combined, LTT[W4[Int]])
assertNotChild(combined, LTT[W3[Int]])
assertNotChild(combined, LTT[W5[Boolean]])
assertNotChild(combined, LTT[W1 with W5[Boolean]])
}
"support structural & refinement type subtype checks" in {
type C1 = C
assertSameStrict(LTT[{ def a: Int }], LTT[{ val a: Int }])
assertSameStrict(LTT[C { def a: Int }], LTT[C1 { def a: Int }])
assertChildStrict(LTT[C { def a: Int }], LTT[C])
assertChildStrict(LTT[C { type A = Int }], LTT[C])
assertChildStrict(LTT[C { type A <: Int }], LTT[C])
assertChildStrict(LTT[C { def a: Int; def b: Int }], LTT[C { def a: Int }])
assertChildStrict(LTT[C { def a: Int }], LTT[{ def a: Int }])
}
"support structural & refinement type equality" in {
assertDifferent(LTT[W4[str.type] with ({ type T = str.type with Int })], LTT[W4[str.type] with ({ type T = str.type with Long })])
type C1 = C
assertSame(LTT[{ def a: Int }], LTT[{ def a: Int }])
assertSame(LTT[C { def a: Int }], LTT[C1 { def a: Int }])
assertDifferent(LTT[C { def a: Int }], LTT[{ def a: Int }])
assertDifferent(LTT[C { def a: Int }], LTT[C])
assertDifferent(LTT[C { def a: Int }], LTT[C { def a: Int; def b: Int }])
val a1 = new C {
override type A = Int
}
object Z {
type X = { type A = Int }
}
val _ = (a1, Z)
assertSame(LTT[a1.A], LTT[Z.X#A])
}
"strong summons test" in {
assertTypeError("def x1[T] = LTag[Array[Int] { type X = T }]")
assertTypeError("def x1[T] = LTag[Array[T]]")
assertTypeError("def x1[T <: { type Array }] = LTag[T#Array]")
assertTypeError("def x1[T] = LTag[Array[Int] with List[T]]")
assertTypeError("def x1[F[_]] = LTag[F[Int]]")
assertCompiles("def x1 = { object x { type T }; def x1 = LTag[Array[x.T]]; () }")
assertCompiles("def x1 = { object x { type T }; LTag[Array[Int] { type X = x.T }]; () }")
assertCompiles("def x1 = { object x { type T }; LTag[Array[Int] with List[x.T]]; () }")
assertCompiles("def x1 = { object x { type F[_] }; LTag[x.F[Int]]; () }")
assertCompiles("def x1 = { object x { type F[_[_]]; type Id[A] = A }; LTag[x.F[x.Id]]; () }")
}
"distinguishes between val and type structural refinements" in {
val t1 = LTT[{ type T = Either[Int, String] }]
val t2 = LTT[{ val T: Either[Int, String] }]
assertNotChildStrict(t1, t2)
}
"does not contain intersections in plain structural refinements" in {
val t1 = LTT[Any { type T = Int }]
val t2 = LTT[{ def x: Int }]
assert(!t1.debug().contains("&"))
assert(!t2.debug().contains("&"))
}
"support equal-bounded types as paradoxical (before 2.3.0 and since 2.3.6 NOT equal to their underlying)" in {
object x {
type X >: String <: String
}
val tag = LTT[String]
val tag1 = LTT[x.X]
// equal bounds create a paradox where s <:< t && t <:< s but not s == t, because refs are not the same.
// but this is also technically true in Scala. equal bounded abstract types are not identical - have their own
// implicit scope, etc. And because in practical usage it's useful to permit these abstract types we're fine with
// representing them despite them breaking our model.
assertChild(tag, tag1)
assertChild(tag1, tag)
assertDifferent(tag1, tag) // paradox and bad, but also an inevitable result of using "optimistic equality" with binary strings
}
"support structural subtype checks" in {
assertNotChildStrict(LTT[{ type T = List[Int] }], LTT[{ type T[A] = List[A] }])
assertChildStrict(LTT[{ type T = List[Int] }], LTT[{ type T <: List[Any] }])
assertChildStrict(LTT[{ type T = Int }], LTT[{ type T <: AnyVal }])
assertChildStrict(LTT[{ type T = Int }], LTT[{ type T <: Any }])
assertChildStrict(LTT[{ type T = String }], LTT[{ type T <: CharSequence }])
assertChildStrict(LTT[{ def T: Int }], LTT[{ def T: AnyVal }])
assertChildStrict(LTT[{ type T = Int }], LTT[{ type T <: AnyVal }])
assertNotChild(LTT[{ type T = Int }], LTT[{ type T <: CharSequence }])
assertNotChildStrict(LTT[{ def T: Int }], LTT[{ type T }])
}
"what about non-empty refinements with intersections" in {
val ltt = LTT[Int with Object with Option[String] { def a: Boolean }]
val debug = ltt.debug()
assert(!debug.contains(""))
assert(!debug.contains(""))
assert(!debug.contains("* String"))
assert(debug.contains("- java.lang.String"))
}
"support contravariance in refinement method comparisons" in {
val t1 = LTT[{ def compare(a: AnyVal): Int }]
val t2 = LTT[{ def compare(b: Int): Int }]
assertChildStrict(t1, t2)
}
"support human-readable representation" in {
type TX[B] = Int { def a(k: String): Int; val b: String; type M1 = W1; type M2 <: W2; type M3[A] = Either[B, A] }
val txTag = `LTT[_]`[TX]
assert(
(txTag.toString // Scala 2
== "λ %0 → (Int {def a(String): Int, def b(): String, type M1 = TestModel::W1, type M2 = M2|, type M3 = λ %2:0 → Either[+0,+2:0]})")
|| (txTag.toString // Dotty
== "λ %0 → (Int {def a(String): Int, def b(): String, type M1 = TestModel::W1, type M2 = M2|, type M3 = λ %1:0 → Either[+0,+1:0]})")
)
val txCombinedTag = `LTT[_]`[TX].combine(LTT[Unit])
assert(
(txCombinedTag.toString // Scala 2
== "(Int {def a(String): Int, def b(): String, type M1 = TestModel::W1, type M2 = M2|, type M3 = λ %2:0 → Either[+Unit,+2:0]})")
|| (txCombinedTag.toString // Dotty
== "(Int {def a(String): Int, def b(): String, type M1 = TestModel::W1, type M2 = M2|, type M3 = λ %1:0 → Either[+Unit,+1:0]})")
)
val txUnitTag = LTT[TX[Unit]]
assert(
(txUnitTag.toString
== "(Int {def a(String): Int, def b(): String, type M1 = TestModel::W1, type M2 = M2|, type M3 = λ %1:0 → Either[+Unit,+1:0]})")
|| (txUnitTag.toString
== "(Int {def a(String): Int, def b(): String, type M1 = TestModel::W1, type M2 = M2|, type M3 = λ %0 → Either[+Unit,+0]})")
)
assertRepr(LTT[I1 with (I1 with (I1 with W1))], "{TestModel::I1 & TestModel::W1}")
assertRepr(`LTT[_]`[R1], "λ %0 → TestModel::R1[=0]")
assertRepr(`LTT[_]`[Nothing], "Nothing")
assertRepr(LTT[Int], "Int")
assertRepr(LTT[List[Int]], "List[+Int]")
assertRepr(LTT[Id[Int]], "Int")
assertRepr(LTT[FP[Int]], "List[+Int]")
assertRepr(`LTT[_]`[L], "λ %0 → List[+0]")
assertRepr(`LTT[_]`[Either[Unit, *]], "λ %0 → Either[+Unit,+0]")
assertRepr(`LTT[_]`[S[Unit, *]], "λ %0 → Either[+0,+Unit]")
}
"covariance of a concrete inheritor to a parent with a higher-kinded type parameter" in {
trait Container[T] {
def tt(): T
}
final case class AContainer[T](t: T) extends Container[T] {
override def tt(): T = t
}
trait Service[+O[x] <: Container[x]] {
def giveHello: String
}
final case class MyConcreteService() extends Service[AContainer] {
def giveHello = "Concrete hello!"
}
assertChild(`LTT[_]`[AContainer], `LTT[_]`[Container])
assertChild(LTT[AContainer[Int]], LTT[Container[Int]])
assertChild(LTT[AContainer[Int]], LTT[Container[_]])
assertNotChild(LTT[AContainer[Int]], LTT[Container[Any]])
val concrete = LTT[MyConcreteService]
val parent = LTT[Service[Container]]
val appliedParent = `LTT[A[_],_[_[x] <: A[x]]`[Container, Service].combine(`LTT[_]`[Container])
assertChild(concrete, appliedParent)
implicitly[MyConcreteService <:< Service[Container]] // true
withSanityChecks {
assertChild(concrete, parent)
}
}
"covariance of a concrete inheritor to a parent with a complex-shaped higher-kinded type parameter" in {
trait Container[T] {
def tt(): T
}
final case class AContainer[T, Y](t: T) extends Container[T] {
override def tt(): T = t
}
trait Service[+O[x] <: Container[x]] {
def giveHello: String
}
final case class MyConcreteService() extends Service[AContainer[*, Int]] {
def giveHello = "Concrete hello!"
}
assertChild(`LTT[_]`[AContainer[*, Unit]], `LTT[_]`[Container])
assertChild(LTT[AContainer[Int, Unit]], LTT[Container[Int]])
assertChild(LTT[AContainer[Int, Unit]], LTT[Container[_]])
assertNotChild(LTT[AContainer[Int, Unit]], LTT[Container[Any]])
val concrete = LTT[MyConcreteService]
val parent = LTT[Service[Container]]
val appliedParent = `LTT[A[_],_[_[x] <: A[x]]`[Container, Service].combine(`LTT[_]`[Container])
assertChild(concrete, appliedParent)
implicitly[MyConcreteService <:< Service[Container]] // true
withSanityChecks {
assertChild(concrete, parent)
}
}
"covariance of a concrete inheritor to a parent with a swap type lambda" in {
trait Container[T, Y]
final case class AContainer[T, Y]() extends Container[Y, T]
type SwappedAContainer[T, Y] = AContainer[Y, T]
trait Service[+O[x, y] <: Container[x, y]] {
def giveHello: String
}
final case class MyConcreteService() extends Service[SwappedAContainer] {
def giveHello = "Concrete hello!"
}
assertChild(`LTT[_]`[AContainer[Int, *]], `LTT[_]`[Container[*, Int]])
assertChild(LTT[AContainer[Int, Unit]], LTT[Container[Unit, Int]])
assertChild(LTT[AContainer[Int, Unit]], LTT[Container[_, _]])
assertNotChild(LTT[AContainer[Int, Unit]], LTT[Container[Any, Any]])
val concrete = LTT[MyConcreteService]
val parent = LTT[Service[Container]]
implicitly[MyConcreteService <:< Service[Container]] // true
withSanityChecks {
assertChild(concrete, parent)
}
}
"covariance of a concrete inheritor to a parent with a proper type parameter" in {
trait Container {
def tt(): Any
}
final case class AContainer(t: Any) extends Container {
override def tt(): Any = t
}
trait Service[+O] {
def giveHello: String
}
final case class MyConcreteService() extends Service[AContainer] {
def giveHello = "Concrete hello!"
}
assertChild(LTT[AContainer], LTT[Container])
val concrete = LTT[MyConcreteService]
val parent = LTT[Service[Container]]
implicitly[MyConcreteService <:< Service[Container]] // true
assertChild(concrete, parent)
}
"covariance of a parameterized inheritor to a parent with an indirect proper type parameter" in {
trait Container {
def tt(): Any
}
final case class AContainer(t: Any) extends Container {
override def tt(): Any = t
}
trait Service[I, +O] {
def giveHello: String
}
final case class MyParameterizedService[T]() extends Service[T, AContainer] {
def giveHello = "Concrete hello!"
}
assertChild(LTT[AContainer], LTT[Container])
val concrete = LTT[MyParameterizedService[Int]]
val parent = LTT[Service[Int, Container]]
implicitly[MyParameterizedService[Int] <:< Service[Int, Container]] // true
assertChild(concrete, parent)
}
"covariance of a parameterized inheritor to a parent with an indirect parameterized type parameter" in {
trait Container[+T] {
def tt(): T
}
final case class AContainer[+T](t: T) extends Container[T] {
override def tt(): T = t
}
trait Service[I, +O] {
def giveHello: String
}
final case class MyParameterizedService[T]() extends Service[T, AContainer[Int]] {
def giveHello = "Concrete hello!"
}
assertChild(LTT[AContainer[Int]], LTT[Container[AnyVal]])
val concrete = LTT[MyParameterizedService[Int]]
val parent = LTT[Service[Int, Container[AnyVal]]]
implicitly[MyParameterizedService[Int] <:< Service[Int, Container[AnyVal]]] // true
assertChild(concrete, parent)
}
"covariance of a parameterized inheritor to a parent with an indirect parameterized type parameter with different arity" in {
trait Container[I, +T] {
def tt(): T
}
final case class AContainer[+T](t: T) extends Container[Int, T] {
override def tt(): T = t
}
trait Service[I, +O] {
def giveHello: String
}
final case class MyParameterizedService[T]() extends Service[T, AContainer[Int]] {
def giveHello = "Concrete hello!"
}
assertChild(LTT[AContainer[Int]], LTT[Container[Int, AnyVal]])
val concrete = LTT[MyParameterizedService[Int]]
val parent = LTT[Service[Int, Container[Int, AnyVal]]]
implicitly[MyParameterizedService[Int] <:< Service[Int, Container[Int, AnyVal]]] // true
assertChild(concrete, parent)
}
"covariance of a self-parameterized inheritor to a parent with an indirect parameterized type parameter with different arity" in {
trait Container[I, +T] {
def tt(): T
}
final case class AContainer[+T](t: T) extends Container[Int, T] {
override def tt(): T = t
}
trait Service[+I, +O] {
def giveHello: String
}
final case class MyParameterizedService[T]() extends Service[AContainer[T], AContainer[AContainer[T]]] {
def giveHello = "Concrete hello!"
}
val tag = LTT[AContainer[AContainer[Int]]]
val parent0 = LTT[Container[Int, Container[Int, AnyVal]]]
implicitly[AContainer[AContainer[Int]] <:< Container[Int, Container[Int, AnyVal]]]
assertChild(tag, parent0)
val concrete = LTT[MyParameterizedService[Int]]
val parent = LTT[Service[Container[Int, AnyVal], Container[Int, Any]]]
implicitly[MyParameterizedService[Int] <:< Service[Container[Int, AnyVal], Container[Int, Any]]] // true
assertChild(concrete, parent)
}
"regression test for https://github.com/zio/izumi-reflect/issues/511" in {
class Foo5[-A, B, -C, +DXX, +E]
class Foo10[F[_], +G[_], -H[-_, +_], I, +J[+_, +_], B, -A, -C, +E, +DXX] extends Foo5[A, B, C, DXX, E]
val tag1 = LTT[Foo5[Int, String, Double, Float, Long]]
assert(tag1.ref.isInstanceOf[FullReference])
assert(
tag1.ref.asInstanceOf[FullReference].parameters.map(_.variance)
== List(Variance.Contravariant, Variance.Invariant, Variance.Contravariant, Variance.Covariant, Variance.Covariant)
)
val tag2 = LTT[Foo10[Option, List, Function1, Boolean, Either, Int, String, Double, Float, Long]]
assert(tag2.ref.isInstanceOf[FullReference])
assert(
tag2.ref.asInstanceOf[FullReference].parameters.map(_.variance)
== List(
Variance.Invariant,
Variance.Covariant,
Variance.Contravariant,
Variance.Invariant,
Variance.Covariant,
Variance.Invariant,
Variance.Contravariant,
Variance.Contravariant,
Variance.Covariant,
Variance.Covariant
)
)
}
"null type is supported" in {
assertChildStrict(LTT[Null], LTT[I1])
assertChild(LTT[Nothing], LTT[Null])
}
"fulldb should contain inheritance db of direct type argument" in {
class Box[+T]
val lt = LTT[Box[Int]]
val fulldb = LightTypeTagUnpacker(lt).bases
assert(fulldb(LTT[Int].ref.asInstanceOf[LightTypeTagRef.NameReference]) == Set(LTT[AnyVal].ref))
}
"unapplied inheritance db should contain inheritance db of direct type argument" in {
class Box[+T]
val lt = LTT[Box[Int]]
val inh = LightTypeTagUnpacker(lt).inheritance
assert(inh(LTT[Int].ref.asInstanceOf[LightTypeTagRef.NameReference]) == Set(LTT[AnyVal].ref))
}
"both dbs should contain db of direct self-nested type argument" in {
class Box[+T]
val lt = LTT[Box[Box[Int]]]
val fulldb = LightTypeTagUnpacker(lt).bases
val inh = LightTypeTagUnpacker(lt).inheritance
assert(fulldb(LTT[Int].ref.asInstanceOf[LightTypeTagRef.NameReference]) == Set(LTT[AnyVal].ref))
assert(inh(LTT[Int].ref.asInstanceOf[LightTypeTagRef.NameReference]) == Set(LTT[AnyVal].ref))
}
"fulldb should contain inheritance db of inherited type argument" in {
class Box[+T]
class IndirectBox extends Box[Int]
val lt = LTT[IndirectBox]
val fulldb = LightTypeTagUnpacker(lt).bases
assert(fulldb.contains(LTT[Int].ref.asInstanceOf[LightTypeTagRef.NameReference]))
assert(fulldb(LTT[Int].ref.asInstanceOf[LightTypeTagRef.NameReference]) == Set(LTT[AnyVal].ref))
}
"unapplied inheritance db should contain inheritance db of inherited type argument" in {
class Box[+T]
class IndirectBox extends Box[Int]
val lt = LTT[IndirectBox]
val inh = LightTypeTagUnpacker(lt).inheritance
assert(inh.contains(LTT[Int].ref.asInstanceOf[LightTypeTagRef.NameReference]))
assert(inh(LTT[Int].ref.asInstanceOf[LightTypeTagRef.NameReference]) == Set(LTT[AnyVal].ref))
}
// Inheritance DB of parent itself is almost certainly not necessary for comparisons
// - if compared to a tag of parent type, that tag will already have a db for parent type.
"both dbs should NOT contain inheritance db of direct parent type" in {
case class CaseClass()
val lt = LTT[CaseClass]
val fulldb = LightTypeTagUnpacker(lt).bases
val inh = LightTypeTagUnpacker(lt).inheritance
val ltProduct = LTT[Product]
assert(!fulldb.contains(ltProduct.ref.asInstanceOf[LightTypeTagRef.NameReference]))
assert(!inh.contains(ltProduct.ref.asInstanceOf[LightTypeTagRef.NameReference]))
}
"HKT List dbs should not contain superfluous base types" in {
val lt = `LTT[_]`[List]
val fulldb = LightTypeTagUnpacker(lt).bases
val inh = LightTypeTagUnpacker(lt).inheritance
if (Scala2MinorVersion.scala2MinorVersion == 11 || Scala2MinorVersion.scala2MinorVersion == 12) {
assert(fulldb.keys.size == 4) // ParSeq + Seq
assert(inh.keys.size == 4)
} else {
assert(fulldb.keys.size == 2)
assert(inh.keys.size == 2)
assert(fulldb.keySet.contains(LTT[Int].ref.asInstanceOf[LightTypeTagRef.NameReference]))
assert(inh.keySet.contains(LTT[Int].ref.asInstanceOf[LightTypeTagRef.NameReference]))
assert((fulldb.keySet - LTT[Int].ref.asInstanceOf[LightTypeTagRef.NameReference]).map(_.repr) == Set("λ %0 → scala.collection.immutable.List[+0]"))
assert((inh.keySet - LTT[Int].ref.asInstanceOf[LightTypeTagRef.NameReference]).map(_.repr) == Set("scala.collection.immutable.List"))
}
}
"subtype check succeeds when child type has absorbed a covariant type parameter of the supertype" in {
assertChild(LTT[Set[Int]], LTT[Iterable[AnyVal]])
val tagF3 = LTT[F3]
val tagF2 = LTT[F2[Int]]
assertChildStrict(tagF3, tagF2)
assertChildStrict(tagF3, LTT[F2[Any]])
assertChildStrict(tagF3, LTT[F2[AnyVal]])
}
"regression test 3.0.4: subtype check succeeds versus a parent parameterized with Identity type lambda" in {
val child = LTT[TargetRole]
val parent = LTT[AbstractRole[Id]]
assertChildStrict(child, parent)
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/SharedTagProgressionTest.scala
================================================
package izumi.reflect.test
import izumi.reflect.macrortti._
import izumi.reflect.test.TestModel._
import izumi.reflect._
import izumi.reflect.test.PlatformSpecific.fromRuntime
import org.scalatest.exceptions.TestFailedException
import org.scalatest.wordspec.AnyWordSpec
import scala.util.Try
/**
* The tests here are *progression* tests, that means they test that something *doesn't work*
*
* If a test here starts to fail that's a GOOD thing - that means a new feature is now supported.
* When that happens you can remove the `broken` condition inversions and move the test to
* the non-progression test suite.
*
* All tests must have `broken` clauses wrapping the expected GOOD conditions if a feature
* were to work. If a test is missing `broken` clause, it's a probably not a progression test
* anymore and should be moved.
*/
abstract class SharedTagProgressionTest extends AnyWordSpec with TagAssertions with TagProgressions with InheritedModel {
"[progression] Tag (all versions)" should {
"progression test: can't substitute type parameters inside defs/vals in structural types" in {
def t1[T: Tag]: Tag[{ def x: T }] = Tag[{ def x: T }]
def t2[T: Tag]: Tag[{ val x: T }] = Tag[{ val x: T }]
broken {
assertSameStrict(t1[Int].tag, Tag[{ def x: Int }].tag)
}
broken {
assertSameStrict(t2[Int].tag, Tag[{ val x: Int }].tag)
}
}
"progression test: cannot resolve a higher-kinded type in a higher-kinded tag in a named deeply-nested type lambda on Scala 2" in {
val t = Try(intercept[TestFailedException] {
assertCompiles(
"""
def mk[F[+_, +_]: TagKK] = TagKK[({ type l[A, B] = BIOServiceL[F, A, B] })#l]
val tag = mk[Either]
assert(tag.tag == LTagKK[({ type l[E, A] = BIOService[ ({ type l[X, Y] = Either[A, E] })#l ] })#l].tag)
"""
)
})
brokenOnScala2 {
assert(t.isFailure)
}
}
"progression test: cannot resolve a higher-kinded type in a higher-kinded tag in an anonymous deeply-nested type lambda" in {
val t = intercept[TestFailedException] {
assertCompiles(
"""
def mk[F[+_, +_]: TagKK] = TagKK[ ({ type l[E, A] = BIOService[ ({ type l[X, Y] = F[A, E] })#l ] })#l ]
val tag = mk[Either]
assert(tag.tag == LTagKK[ ({ type l[E, A] = BIOService[ ({ type l[X, Y] = Either[A, E] })#l ] })#l ].tag)
"""
)
}
assert(
t.getMessage.contains("could not find implicit value") ||
t.getMessage.contains("diverging implicit") || /*2.11*/
t.getMessage.contains("no implicit argument of type") || /*Dotty*/
t.getMessage.contains("Cannot find implicit Tag") /*Dotty 3.1.3+*/
)
}
"progression test: projections into singletons are not handled properly (on Scala 2)" in {
trait A {
class X
final val singleton1 = "bar"
type S1 = singleton1.type
val singleton2 = "bar"
type S2 = singleton2.type
// val s1a = Tag[S1] // class type required but String("bar") found error on 2.11
val s1a = LTT[S1]
val s1a1 = Tag[singleton1.type].tag
// val s2a = Tag[S2]
val s2a = LTT[S2]
val s2a1 = Tag[singleton2.type].tag
}
trait B extends A {
val xb = Tag[X].tag
// val s1b = Tag[S1].tag
val s1b = LTT[S1]
val s1b1 = Tag[singleton1.type].tag
val s2b = LTT[S2]
val s2b1 = Tag[singleton2.type].tag
}
object B extends B
// Scala 2.12 doesn't handle literal types here
if (LTT[B.singleton1.type] != LTT[String] && !IsScala3) {
assertDifferent(Tag[A#S1].tag, LTT[String])
}
assertSame(Tag[A#S1].tag, B.s1a)
assertSame(Tag[A#S1].tag, B.s1a1)
assertSame(Tag[A#S1].tag, B.s1b)
assertSame(Tag[A#S1].tag, B.s1b1)
// progression: this still fails; see https://github.com/zio/izumi-reflect/issues/192
// projection into singleton generates a form `_1.singleton2.type forSome { val _1: A }` which is not handled on Scala 2
brokenOnScala2 {
assertSame(Tag[A#S2].tag, B.s2a)
}
brokenOnScala2 {
assertSame(Tag[A#S2].tag, B.s2b)
}
brokenOnScala2 {
assertSame(Tag[A#S2].tag, B.s2a1)
}
brokenOnScala2 {
assertSame(Tag[A#S2].tag, B.s2b1)
}
}
"Progression test: Scala 2 partially fails to Handle Tags outside of a predefined set when using TagX alias (Tag.auto.T works directly)" in {
type TagX[F[_, _, _[_[_], _], _[_], _]] = Tag.auto.T[F]
brokenOnScala2 {
assertCompiles(
"""
def testTagX[F[_, _, _[_[_], _], _[_], _]: TagX, A: Tag, B: Tag, C[_[_], _]: TagTK, D[_]: TagK, E: Tag]: Tag[F[A, B, C, D, E]] = Tag[F[A, B, C, D, E]]
"""
)
}
def testTagXDirect[F[_, _, _[_[_], _], _[_], _]: Tag.auto.T, A: Tag, B: Tag, C[_[_], _]: TagTK, D[_]: TagK, E: Tag]: Tag[F[A, B, C, D, E]] = Tag[F[A, B, C, D, E]]
val value = testTagXDirect[TXU, Int, String, OptionT, List, Boolean]
assert(value.tag == fromRuntime[TXU[Int, String, OptionT, List, Boolean]])
}
"progression test: fails to combine higher-kinded intersection types without losing ignored type arguments" in {
def mk[F[+_, +_]: TagKK, G[+_, +_]: TagKK] = Tag[IntersectionBlockingIO[F, G]]
val tag = mk[Either, IO]
val tagMono = Tag[IntersectionBlockingIO[Either, IO]]
broken {
assertSameStrict(tag.tag, tagMono.tag)
}
}
"progression test: Dotty fails to regression test: resolve correct closestClass for Scala vararg AnyVal (https://github.com/zio/izumi-reflect/issues/224)" in {
val tag = Tag[VarArgsAnyVal]
brokenOnScala3 {
assert(tag.closestClass == classOf[scala.Seq[Any]])
}
}
"progression test: fails to preserve lower bound when combining higher-kinded type members" in {
def combine1[X[_[_], _]: TagTK, F[_]: TagK, A: Tag]: Tag[X[F, A]] = Tag[X[F, A]]
def combine2[F[_]: TagK, A: Tag]: Tag[F[A]] = Tag[F[A]]
val t1 = TagTK[HigherKindedTypeMember.T]
val t2 = TagK[HigherKindedTypeMember.T[IO[Throwable, *], *]]
val tres1 = combine1[HigherKindedTypeMember.T, IO[Throwable, *], Int](t1, implicitly, implicitly)
val tres2 = combine2[HigherKindedTypeMember.T[IO[Throwable, *], *], Int](t2, implicitly)
broken {
assertChildStrict(Tag[Unit].tag, tres1.tag)
assertChildStrict(Tag[Unit].tag, tres2.tag)
}
}
// Can't fix this atm because simplification happens even without .simplified call because of https://github.com/lampepfl/dotty/issues/17544
// The other way to fix this is to call `LightTypeTag.removeIntersectionTautologies` for every `Tag.refinedTag` -
// but this would make such tags expensive to construct because the complexity is quadratic, with two <:< calls
// per iteration.
"progression test: fails on Scala 3 don't lose tautological intersection components other than Any/AnyRef" in {
def tag1[T: Tag]: Tag[T with Trait1] = Tag[T with Trait1]
def tag4[T: Tag]: Tag[T with Trait4] = Tag[T with Trait4]
val t1 = tag1[Trait3[Dep]].tag
val t2 = tag4[Trait3[Dep]].tag
val t10 = Tag[Trait3[Dep] with Trait1].tag
val t20 = Tag[Trait3[Dep] with Trait4].tag
brokenOnScala3 {
assertSameStrict(t1, t10)
assertDebugSame(t1, t10)
}
assertSameStrict(t2, t20)
assertDebugSame(t2, t20)
}
// We don't really want to fix it, because removing tautologies is quadratic, with two subtyping comparisions per step!
// Would make construction really expensive, all for an extremely rare corner case
"progression test: intersection tautologies are not removed automatically when constructing combined intersection type" in {
def tag1[T: Tag]: Tag[T with Trait1] = Tag[T with Trait1]
val t1 = tag1[Trait3[Dep]].tag
val t10 = t1.removeIntersectionTautologies
broken {
assertSameStrict(t1, t10)
assertDebugSame(t1, t10)
}
}
"progression test: null is treated like Nothing, not like a separate type" in {
assert(LTT[Null] <:< LTT[Int])
assert(LTT[Null] <:< LTT[Nothing])
}
"progression test: parameter resolution breaks inside covariant wildcard type bounds on Scala 2.13" in {
def xcov[T[_]: TagK, U: Tag]: Tag[List[_ <: T[U]]] = Tag[List[_ <: T[U]]]
brokenOnScala2MinorVersion(13) {
assertRepr(xcov[List, Long].tag, "List[+?: ]")
assertSameStrict(xcov[List, Long].tag, Tag[List[_ <: List[Long]]].tag)
}
}
"progression test: combine intersection path-dependent intersection types with inner tags doesn't work on Scala 2" in {
trait PDT {
type T
implicit def tag: Tag[T]
def badCombine(that: PDT): Tag[T with that.T] = {
Tag[T with that.T]
}
}
brokenOnScala2 {
assertCompiles(
"""
trait PDT0 {
type T
implicit def tag: Tag[T]
def goodCombine(that: PDT): Tag[this.T with that.T] = {
import that.tag
Tag[this.T with that.T]
}
}"""
)
}
def PDT[U: Tag]: PDT = new PDT { type T = U; override val tag: Tag[U] = Tag[U] }
val badCombine = PDT[Int].badCombine(PDT[Unit])
brokenOnScala2 {
assertSameStrict(badCombine.tag, Tag[Int with Unit].tag)
}
}
"progression test: `combine inside type lambdas where the type constructor of the type lambda result is a type lambda type parameter` doesn't work" in {
val res = Try(assertCompiles("""
def mk[T: Tag] = Tag[LambdaParamCtorBlockingIOT[T]]
val tag = mk[Int]
val tagMono = Tag[LambdaParamCtorBlockingIOT[Int]]
assertSameStrict(tag.tag, tagMono.tag)
"""))
broken {
assert(res.isSuccess)
}
broken {
assert(
!(res.failed.get.getMessage.contains("Error when creating a combined tag")
|| res.failed.get.getMessage.contains("could not find implicit value for izumi.reflect.Tag"))
)
}
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/SharedTagTest.scala
================================================
package izumi.reflect.test
import izumi.reflect.macrortti.LightTypeTag.ParsedLightTypeTag230Plus
import izumi.reflect.macrortti._
import izumi.reflect.test.ID._
import izumi.reflect.test.TestModel._
import izumi.reflect.thirdparty.internal.boopickle.PickleImpl
import izumi.reflect._
import izumi.reflect.test.DiscoveryModel.{DiscoverableService, DiscoverableServiceImpl, DiscoveryNodeProvider, GetDiscoveryNode, NodeIdImpl}
import izumi.reflect.test.TestModel.x.SrcContextProcessor
import org.scalatest.Assertions
import org.scalatest.exceptions.TestFailedException
import org.scalatest.wordspec.AnyWordSpec
import scala.annotation.StaticAnnotation
import scala.collection.immutable.Set
import scala.collection.mutable
import scala.util.Try
object ID {
type id[A] = A
type Identity[+A] = A
}
trait Clock
object ClockLive extends Clock
trait ZY extends Assertions {
type T
type U = T
type V = List[T]
type A = List[Option[Int]]
type O <: List[T]
val x: String = "5"
object y
trait Y
val tagT = intercept[TestFailedException](assertCompiles("izumi.reflect.Tag[T]"))
val tagU = intercept[TestFailedException](assertCompiles("izumi.reflect.Tag[U]"))
val tagV = intercept[TestFailedException](assertCompiles("izumi.reflect.Tag[V]"))
val tagA = Try(assertCompiles("izumi.reflect.Tag[A]"))
}
trait XY[Y] {
type Z = id[Y]
implicit def tagZ: Tag[Z]
}
case class OptionT[F[_], A](value: F[Option[A]])
final case class testTag[T: Tag]() {
type X[A] = Either[Int, A]
type Y = T
val res = Tag[X[Y {}]]
}
final case class testTag2[T: Tag]() {
type X = List[T]
val res = Tag[X]
}
trait TXU[A, B, C[_[_], _], D[_], E]
abstract class SharedTagTest extends AnyWordSpec with XY[String] with TagAssertions with InheritedModel {
type Abstract
type Id[A] = A
type Id1[F[_], A] = F[A]
import izumi.reflect.test.PlatformSpecific.fromRuntime
final val str = "str"
final class With[T] extends StaticAnnotation
case class ZOBA[A, B, C](value: Either[B, C])
trait Test[A, dafg, adfg, LS, L[_], SD, GG[B] <: L[B], ZZZ[_, _], S, SDD, TG]
trait T1[A, B, C, D, E, F[_]]
trait YX[V] extends XY[V]
trait DockerContainer[T]
trait ContainerDef {
type T
def make(implicit t: Tag[T]) = {
val _ = t
Tag[DockerContainer[T]]
}
}
"Tag (all versions)" should {
"Work for any concrete type" in {
assert(Tag[Int].tag == fromRuntime[Int])
assert(Tag[Set[String]].tag == fromRuntime[Set[String]])
assert(Tag[Map[Boolean, Double]].tag == fromRuntime[Map[Boolean, Double]])
assert(Tag[_ => Unit].tag == fromRuntime[_ => Unit])
assert(Tag[Unit => _].tag == fromRuntime[Unit => _])
assert(Tag[_ => _].tag == fromRuntime[_ => _])
assert(Tag[Any].tag == fromRuntime[Any])
assert(Tag[Nothing].tag == fromRuntime[Nothing])
assert(Tag[Any => Nothing].tag == fromRuntime[Any => Nothing])
assert(Tag[Nothing => Any].tag == fromRuntime[Nothing => Any])
assert(Tag[With[Any]].tag == fromRuntime[With[Any]])
assert(Tag[With[Nothing]].tag == fromRuntime[With[Nothing]])
assert(Tag[With[_]].tag == fromRuntime[With[_]])
assert(Tag[Int with String].tag == fromRuntime[Int with String])
assert(Tag[str.type].tag == fromRuntime[str.type])
assert(Tag[this.Z].tag == fromRuntime[this.Z])
assert(Tag[TagTest#Z].tag == fromRuntime[TagTest#Z])
}
"Support identity lambda type equality" in {
val idTpeLTT = TagK[Identity].tag
val idLambdaLTT = TagK[λ[A => A]].tag
assert(idTpeLTT == idLambdaLTT)
}
"regression test for https://github.com/zio/izumi-reflect/issues/98" in {
object SomeService {
trait Service[T]
final case class Foo()
val tag1: Tag[Service[Foo]] = Tag[Service[Foo]]
}
object IzumiReflectTagEqualRegression {
import SomeService._
def test(): Unit = {
val tag1: Tag[Service[Foo]] = SomeService.tag1
val tag2: Tag[Service[Foo]] = Tag[Service[Foo]]
val rtTag1 = PickleImpl.serializeIntoString(tag1.tag.ref, LightTypeTag.lttRefSerializer)
val rtTag2 = PickleImpl.serializeIntoString(tag2.tag.ref, LightTypeTag.lttRefSerializer)
assert(tag1.tag.ref == tag2.tag.ref)
assert(rtTag1 == tag1.tag.asInstanceOf[ParsedLightTypeTag230Plus].refString)
assert(rtTag2 == tag2.tag.asInstanceOf[ParsedLightTypeTag230Plus].refString)
assert(rtTag1 == rtTag2)
assert(tag1.tag == tag2.tag)
()
}
}
IzumiReflectTagEqualRegression.test()
}
"Work for any abstract type with available Tag when obscured by empty refinement" in {
def testTag[T: Tag] = Tag[T {}]
assert(testTag[String].tag == fromRuntime[String])
}
"handle function local type aliases" in {
def testTag[T: Tag] = {
type X[A] = Either[Int, A]
Tag[X[T {}]]
}
assert(testTag[String].tag == fromRuntime[Either[Int, String]])
def testTag2[T: Tag] = {
type X = List[T]
Tag[X]
}
assert(testTag2[String].tag == fromRuntime[List[String]])
def testTag3[F[_]: TagK] = {
type X = OptionT[F, Int]
Tag[X]
}
assert(testTag3[List].tag == fromRuntime[OptionT[List, Int]])
}
"Can dealias transparent type members with class type parameters inside them when a tag is summoned _inside_ the class, because LightTypeTags are not affected by https://github.com/scala/bug/issues/11139" in {
assert(testTag[String]().res.tag == fromRuntime[Either[Int, String]])
assert(testTag2[String]().res.tag == fromRuntime[List[String]])
assert(testTag3[List]().res == fromRuntime[OptionT[List, Int]])
}
"Tag.auto.T kind inference macro works for known cases" in {
def x[T[_]: Tag.auto.T]: TagK[T] = implicitly[Tag.auto.T[T]]
def x2[T[_, _]: Tag.auto.T]: TagKK[T] = implicitly[Tag.auto.T[T]]
def x3[T[_, _, _[_[_], _], _[_], _]](implicit x: Tag.auto.T[T]): Tag.auto.T[T] = x
val b1 = x[Option].tag =:= TagK[Option].tag
val b2 = x2[Either].tag =:= TagKK[Either].tag
val b3 = implicitly[Tag.auto.T[OptionT]].tag =:= TagTK[OptionT].tag
val b4 = x3[TXU].tag.withoutArgs =:= LTag[TXU[Nothing, Nothing, Nothing, Nothing, Nothing]].tag.withoutArgs
assert(b1)
assert(b2)
assert(b3)
assert(b4)
}
"support HKTag for unapplied type lambdas with type bounds" in {
trait X
trait XAble[A <: X]
class Y extends X
def getTag[F[A <: X]: Tag.auto.T] = {
val _ = implicitly[Tag.auto.T[F]]
Tag[F[Y]]
}
val tag = getTag[XAble]
assert(tag.tag == Tag[XAble[Y]].tag)
}
"support Tag.auto.T for higher-kinded type lambdas with interdependent inner and outer type bounds" in {
class Dep0
trait Trait30[T <: Dep0]
trait TraitK30[T[x <: Dep0] <: Trait30[x]]
def t[K[F[x <: Dep0] <: Trait30[x]]: Tag.auto.T, T[x <: Dep0] <: Trait30[x]: Tag.auto.T] = Tag[K[T]]
assert(t[TraitK30, Trait30].tag == Tag[TraitK30[Trait30]].tag)
}
"Shouldn't work for any abstract type without available TypeTag or Tag or TagK" in {
assertTypeError("""
def testTag[T] = Tag[T]
def testTagK[F[_], T] = Tag[F[T]]
""")
}
"handle Id type lambda" in {
assert(TagK[Id].tag == TagK[Id].tag)
assert(TagK[Id].tag != TagTK[Id1].tag)
}
"handle Id1 type lambda" in {
assert(TagTK[Id1].tag == TagTK[Id1].tag)
assert(TagTK[Id1].tag != TagK[Id].tag)
}
"handle singleton types" in {
assertChild(Tag[ClockLive.type].tag, Tag[Clock].tag)
}
"handle nested intersection aliases" in {
type Inner = Int with String
type Outer = Boolean with Inner
assertChild(Tag[Outer].tag, Tag[Boolean with Int with String].tag)
assertChild(Tag[Boolean with Int with String].tag, Tag[Outer].tag)
assertSame(Tag[Outer].tag, Tag[Boolean with Int with String].tag)
assertNotChild(Tag[Outer].tag, Tag[Boolean with Int with String with Unit].tag)
assertNotChild(Tag[Boolean with Int with String].tag, Tag[Outer with Unit].tag)
assertChild(Tag[Boolean with Int with String].tag, Tag[CharSequence].tag)
assertChild(Tag[Outer].tag, Tag[CharSequence].tag)
// there should be no refinements or intersections in bases
assert(!Tag[Outer].tag.debug().contains(""))
assert(!Tag[Outer].tag.debug().contains(""))
assert(!Tag[Outer].tag.debug().contains("- {"))
}
"handle nested refined intersection aliases" in {
type Inner = ((Int with (String {})) {}) @IdAnnotation("y")
type Outer = Boolean with (((Inner {}) @IdAnnotation("x")) {})
assertChild(Tag[Outer].tag, Tag[Boolean with Int with String].tag)
assertChild(Tag[Boolean with Int with String].tag, Tag[Outer].tag)
assertSame(Tag[Outer].tag, Tag[Boolean with Int with String].tag)
assertNotChild(Tag[Outer].tag, Tag[Boolean with Int with String with Unit].tag)
assertNotChild(Tag[Boolean with Int with String].tag, Tag[Outer with Unit].tag)
assertChild(Tag[Outer].tag, Tag[CharSequence].tag)
// there should be no refinements or intersections in bases
assert(!Tag[Outer].tag.debug().contains(""))
assert(!Tag[Outer].tag.debug().contains(""))
assert(!Tag[Outer].tag.debug().contains("- {"))
}
"simple combined Tag" in {
def get[F[_]: TagK] = Tag[ApplePaymentProvider[F]]
val tag = get[Identity]
val left = tag.tag
val right = Tag[H1].tag
assertChild(left, right)
}
"consider class member's this-prefix to be the defining template, not the most specific prefix from where the call is happening (deliberate omission of this for better ergonomics in cakes)" in {
trait A {
class X
final val singleton1 = "bar"
type S1 = singleton1.type
val singleton2 = "bar"
type S2 = singleton2.type
val xa = Tag[X].tag
// val s1a = Tag[S1] // class type required but String("bar") found error on 2.11
val s1a = LTT[S1]
val s1a1 = Tag[singleton1.type].tag
// val s2a = Tag[S2]
val s2a = LTT[S2]
val s2a1 = Tag[singleton2.type].tag
}
trait B extends A {
val xb = Tag[X].tag
// val s1b = Tag[S1].tag
val s1b = LTT[S1]
val s1b1 = Tag[singleton1.type].tag
val s2b = LTT[S2]
val s2b1 = Tag[singleton2.type].tag
}
object B extends B
assertSameStrict(B.xa, B.xb)
assertSameStrict(B.s1a, B.s1b)
assertSameStrict(B.s1a1, B.s1b1)
assertSameStrict(B.s2a, B.s2b)
assertSameStrict(B.s2a1, B.s2b1)
assertSameStrict(Tag[A#X].tag, B.xa)
assertSameStrict(B.s1b, B.s1a)
assertSameStrict(B.s1a, B.s1a1)
assertSameStrict(B.s1b, B.s1b1)
}
"Does NOT synthesize Tags for abstract types, but recursively summons Tag[this.Abstract]" in {
// no tag synthesized, there's no Tag[Abstract] unless defined
assertDoesNotCompile("Tag[Abstract]")
locally {
implicit val implicitTag: Tag[Abstract] = Tag[Abstract](Tag[Int].closestClass, Tag[Int].tag)
val tag = Tag[Option[Abstract]]
assertSameStrict(tag.tag.typeArgs.head, implicitTag.tag)
assertSameStrict(tag.tag, TagK[Option].tag.combine(implicitTag.tag))
}
}
"DOES synthesize Tags for abstract types (object X; X.T), does not summon Tag[X.T]" in {
val realTag = Tag[Option[SomeObject.Abstract]]
locally {
implicit val implicitTag: Tag[SomeObject.Abstract] = Tag[SomeObject.Abstract](Tag[Int].closestClass, Tag[Int].tag)
val tag = Tag[Option[SomeObject.Abstract]]
assertDifferent(tag.tag.typeArgs.head, implicitTag.tag)
assertDifferent(tag.tag, TagK[Option].tag.combine(implicitTag.tag))
assertSameStrict(realTag.tag, tag.tag)
}
}
"DOES synthesize Tags for abstract types (trait X; X#T), does not summon Tag[X#T]" in {
val realTag = Tag[Option[SomeTrait#Abstract]]
locally {
implicit val implicitTag: Tag[SomeTrait#Abstract] = Tag[SomeTrait#Abstract](Tag[Int].closestClass, Tag[Int].tag)
val tag = Tag[Option[SomeTrait#Abstract]]
assertDifferent(tag.tag.typeArgs.head, implicitTag.tag)
assertDifferent(tag.tag, TagK[Option].tag.combine(implicitTag.tag))
assertSameStrict(realTag.tag, tag.tag)
}
}
"DOES synthesize Tags for abstract types (val x; x.T), does not summon Tag[x.T]" in {
val x = new SomeTrait {}
val realTag = Tag[Option[x.Abstract]]
locally {
implicit val implicitTag: Tag[x.Abstract] = Tag[x.Abstract](Tag[Int].closestClass, Tag[Int].tag)
val tag = Tag[Option[x.Abstract]]
assertDifferent(tag.tag.typeArgs.head, implicitTag.tag)
assertDifferent(tag.tag, TagK[Option].tag.combine(implicitTag.tag))
assertSameStrict(realTag.tag, tag.tag)
}
}
"Work for an abstract type with available TagK when obscured by empty refinement" in {
def testTagK[F[_]: TagK, T: Tag] = Tag[F[T {}] {}]
assert(testTagK[Set, Int].tag == fromRuntime[Set[Int]])
}
"Work for an abstract type with available TagK when TagK is requested through an explicit implicit" in {
def testTagK[F[_], T: Tag](implicit ev: Tag.auto.T[F]) = {
val _ = ev
Tag[F[T {}] {}]
}
assert(testTagK[Set, Int].tag == fromRuntime[Set[Int]])
}
"Work for an abstract type with available TagKK" in {
def t1[F[_, _]: TagKK, T: Tag, G: Tag] = Tag[F[T, G]]
assert(t1[ZOBA[Int, *, *], Int, String].tag == fromRuntime[ZOBA[Int, Int, String]])
}
"Work for any configuration of parameters" in {
def t1[A: Tag, B: Tag, C: Tag, D: Tag, E: Tag, F[_]: TagK]: Tag[T1[A, B, C, D, E, F]] = Tag[T1[A, B, C, D, E, F]]
type ZOB[A, B, C] = Either[B, C]
assert(
t1[Int, Boolean, ZOB[Unit, Int, Int], TagK[Option], Nothing, ZOB[Unit, Int, *]].tag
== fromRuntime[T1[Int, Boolean, Either[Int, Int], TagK[Option], Nothing, Either[Int, *]]]
)
def t2[A: Tag, dafg: Tag, adfg: Tag, LS: Tag, L[_]: TagK, SD: Tag, GG[A] <: L[A]: TagK, ZZZ[_, _]: TagKK, S: Tag, SDD: Tag, TG: Tag]
: Tag[Test[A, dafg, adfg, LS, L, SD, GG, ZZZ, S, SDD, TG]] =
Tag[Test[A, dafg, adfg, LS, L, SD, GG, ZZZ, S, SDD, TG]]
assert(
t2[
SharedTagTest.this.Z,
SharedTagTest.this.Z,
T1[
ZOB[String, Int, Byte],
String,
String,
String,
String,
List
],
SharedTagTest.this.Z,
XY,
SharedTagTest.this.Z,
YX,
Either,
SharedTagTest.this.Z,
SharedTagTest.this.Z,
SharedTagTest.this.Z
].tag
== fromRuntime[Test[String, String, T1[Either[Int, Byte], String, String, String, String, List], String, XY, String, YX, Either, String, String, String]]
)
}
"handle Swap type lambda" in {
def t1[F[_, _]: TagKK, A: Tag, B: Tag] = Tag[F[A, B]]
assert(t1[Swap, Int, String].tag == fromRuntime[Either[String, Int]])
}
"Assemble from higher than TagKK tags" in {
def tag[T[_[_], _]: TagTK, F[_]: TagK, A: Tag] = Tag[T[F, A]]
assert(tag[OptionT, Option, Int].tag == fromRuntime[OptionT[Option, Int]])
}
"regression test: https://github.com/zio/izumi-reflect/issues/293 assemble tag for Builder[B, Collection[B]]" in {
def tag[B: Tag](implicit tag: Tag[java.util.Collection[B]]) = Tag[mutable.Builder[B, java.util.Collection[B]]]
assertSameStrict(tag[Int].tag, Tag[mutable.Builder[Int, java.util.Collection[Int]]].tag)
}
"combine intersection types" in {
def t1[A: Tag] = Tag[String with A]
def t2[A: Tag, B: Tag] = Tag[A with B]
assertSameStrict(t1[Int].tag, Tag[Int with String].tag)
assertSameStrict(t2[Int, String].tag, Tag[String with Int].tag)
assertSameStrict(t1[String].tag, Tag[String].tag)
assertSameStrict(t2[String, String].tag, Tag[String].tag)
}
"summon HKT Tag for a Java type" in {
assertCompiles("TagK[java.util.Collection]")
}
"regression test: https://github.com/zio/izumi-reflect/issues/76 derive tag for a parametric trait inside object" in {
assertSameStrict(X76.x.tag, Tag[X76.T[Int]].tag)
}
"this.type tags should be generated, but are identical with their class / object tag" in {
val classTag = Tag[ThisPrefixTest.ThisPrefix].tag
val objectTag = Tag[ThisPrefixTest.ThisPrefix.type].tag
val classThisTag = new ThisPrefixTest.ThisPrefix().tag
val objectThisTag = ThisPrefixTest.ThisPrefix.tag
assertDebugSame(classThisTag, classTag)
assertDebugSame(objectThisTag, objectTag)
assertNotChildStrict(classTag, objectTag)
assertNotChildStrict(classTag, objectThisTag)
assertNotChildStrict(classThisTag, objectTag)
assertNotChildStrict(classThisTag, objectThisTag)
}
"this.type should have correct prefix" in {
val classTag = Tag[ThisPrefixTest.ThisPrefix].tag
val objectTag = Tag[ThisPrefixTest.ThisPrefix.type].tag
val classThisTag = new ThisPrefixTest.ThisPrefix().tag
val objectThisTag = ThisPrefixTest.ThisPrefix.tag
assert(classTag.ref.getPrefix.isDefined)
assert(objectTag.ref.getPrefix.isDefined)
assert(classThisTag.ref.getPrefix.isDefined)
assert(objectThisTag.ref.getPrefix.isDefined)
assert(classTag.ref.getPrefix == objectTag.ref.getPrefix)
assert(classTag.ref.getPrefix == classThisTag.ref.getPrefix)
assert(classTag.ref.getPrefix == objectThisTag.ref.getPrefix)
}
"regression test: https://github.com/zio/izumi-reflect/issues/83, convert trifunctor tag to bifunctor tag" in {
import TestModel._
def direct[F[+_, +_]: TagKK] = Tag[BIO2[F]]
def indirectFrom3[F[-_, +_, +_]: TagK3] = direct[F[Any, +*, +*]]
assertSame(direct[ZIO[Any, +*, +*]].tag, indirectFrom3[ZIO].tag)
}
"resolve TagK from TagKK" in {
def getTag[F[+_, +_]: TagKK] = TagK[F[Throwable, *]]
val tagEitherThrowable = getTag[Either].tag
val tag = TagK[Either[Throwable, *]].tag
assertSameStrict(tagEitherThrowable, tag)
assertChildStrict(tagEitherThrowable, TagK[Either[Any, *]].tag)
assertChildStrict(TagK[Either[Nothing, *]].tag, tagEitherThrowable)
}
"can materialize TagK for type lambdas that close on a generic parameter with available Tag" in {
def partialEitherTagK[A: Tag] = TagK[Either[A, *]]
val tag = partialEitherTagK[Int].tag
val expectedTag = TagK[Either[Int, *]].tag
assert(tag =:= expectedTag)
}
"can materialize TagK for type lambdas that close on a generic parameter with available Tag when the constructor is a type parameter" in {
def partialFTagK[F[_, _]: TagKK, A: Tag] = TagK[F[A, *]]
val tag = partialFTagK[Either, Int].tag
val expectedTag = TagK[Either[Int, *]].tag
assert(tag =:= expectedTag)
}
"type parameter covariance works after combine" in {
def getTag[F[+_, +_]: TagKK] = TagK[F[Throwable, *]]
val tagEitherThrowable = getTag[Either].tag
val tagEitherSerializable = TagK[Either[java.io.Serializable, *]]
assert(tagEitherThrowable <:< tagEitherSerializable.tag)
}
"combine Const Lambda to TagK" in {
def get[F[_, _]: TagKK] = TagK[F[Int, *]]
val tag = get[Const]
assert(tag.tag =:= TagK[Const[Int, *]].tag)
assert(tag.tag <:< TagK[Const[AnyVal, *]].tag)
assert(tag.tag.hashCode() == TagK[Const[Int, *]].tag.hashCode())
}
"combined TagK 3 & 2 parameter coherence" in {
def get[F[+_, +_]: TagKK] = TagK[F[Throwable, *]]
val tag = get[IO]
assert(tag.tag =:= TagK[IO[Throwable, *]].tag)
assert(tag.tag <:< TagK[IO[Throwable, *]].tag)
assert(tag.tag <:< TagK[IO[Any, *]].tag)
}
"resolve TagKK from an odd higher-kinded Tag with swapped & ignored parameters" in {
def getTag[F[-_, +_, +_]: TagK3] = TagKK[F[*, *, Throwable]]
val tagEitherSwap = getTag[EitherRSwap].tag
val tagEitherThrowable = getTag[EitherR].tag
val expectedTagSwap = TagKK[EitherRSwap[*, *, Throwable]].tag
val expectedTagEitherThrowable = TagKK[EitherR[*, *, Throwable]].tag
assert(!(tagEitherSwap =:= expectedTagEitherThrowable))
assert(tagEitherSwap =:= expectedTagSwap)
assert(tagEitherThrowable =:= expectedTagEitherThrowable)
assert(tagEitherSwap <:< expectedTagSwap)
assert(tagEitherSwap <:< TagKK[EitherRSwap[*, *, Any]].tag)
assert(TagKK[EitherRSwap[*, *, Nothing]].tag <:< tagEitherSwap)
}
"can resolve Tags of TagK's themselves correctly" in {
trait X[A, B, C]
def tagk[F[_]: TagK]: Tag[TagK[F]] = Tag[TagK[F]]
def tagkk[F[_, _]: TagKK]: Tag[TagKK[F]] = Tag[TagKK[F]]
def tagk3[F[_, _, _]: TagK3]: Tag[TagK3[F]] = Tag[TagK3[F]]
def tagtk[F[_[_], _]: TagTK]: Tag[TagTK[F]] = Tag[TagTK[F]]
assertChild(tagk[List].tag, Tag[TagK[List]].tag)
assertSame(tagkk[Either].tag, Tag[TagKK[Either]].tag)
assertSame(tagk3[X].tag, Tag[TagK3[X]].tag)
assertSame(tagtk[OptionT].tag, Tag[TagTK[OptionT]].tag)
}
"regression test: ignore function-local anonymous classes (https://github.com/zio/zio/issues/4285)" in {
class ZIO[-R, +E, +A](val a: Any) {
def map[B](f: A => B): ZIO[R, E, B] = new ZIO(f)
def toLayer[A1 >: A: Tag]: ZLayer[R, E, Has[A1]] = new ZLayer(Tag[Has[A1]])
}
class ZLayer[-R, +E, +A](val t: Tag[_ <: A])
final class Has[X]
type UIO[T] = ZIO[Any, Nothing, T]
def f[T]: UIO[T] = new ZIO(1)
trait S[T] {
val param: T
}
def reproduce[T: Tag]: ZLayer[Any, Nothing, Has[S[T]]] = {
f[T]
.map(
p =>
new S[T] {
override val param: T = p
}
).toLayer
}
assert(reproduce[Unit].t.tag == Tag[Has[S[Unit]]].tag)
}
"equal path-dependent tags for singleton types are expected to be equal" in {
// see https://github.com/zio/izumi-reflect/issues/192
object Foo {
val bar = "bar"
object Bar
val t1 = Tag[bar.type]
val t2 = Tag[Foo.bar.type]
val t3 = Tag[Foo.this.bar.type]
val T1 = Tag[Bar.type]
val T2 = Tag[Foo.Bar.type]
val T3 = Tag[Foo.this.Bar.type]
}
import Foo._
assert(t1.tag =:= t3.tag)
assert(t2.tag =:= t3.tag)
assert(T1.tag =:= T3.tag)
assert(T2.tag =:= T3.tag)
}
"return expected class tag" in {
assert(Tag[List[_] with Set[_]].closestClass eq classOf[scala.collection.immutable.Iterable[_]])
assert(!Tag[List[_] with Set[_]].hasPreciseClass)
assert(Tag[AnyVal].closestClass eq classOf[AnyVal])
assert(!Tag[AnyVal].hasPreciseClass)
assert(Tag[String].closestClass ne classOf[AnyVal])
assert(!Tag[String with Int].hasPreciseClass)
assert(Tag[String with Int].closestClass eq classOf[Any])
assert(Tag[List[Int]].hasPreciseClass)
assert(Tag[List[Int]].closestClass eq classOf[List[_]])
assert(Tag[H1].hasPreciseClass)
assert(Tag[H1].closestClass eq classOf[H1])
assert(!Tag[ZY#T].hasPreciseClass)
assert(Tag[ZY#T].closestClass eq classOf[Any])
assert(Tag[ZY#Y].hasPreciseClass)
assert(Tag[ZY#Y].closestClass eq classOf[ZY#Y])
assert(!Tag[ZY#O].hasPreciseClass)
assert(Tag[ZY#O].closestClass eq classOf[List[ZY#T]])
assert(Tag[Array[Byte]].closestClass eq classOf[Array[Byte]])
assert(Tag[Array[Int]].closestClass eq classOf[Array[Int]])
assert(Tag[Array[AnyRef]].closestClass eq classOf[Array[AnyRef]])
assert(Tag[Array[Boolean]].closestClass eq classOf[Array[Boolean]])
assert(Tag[Array[Char]].closestClass eq classOf[Array[Char]])
assert(Tag[Array[String]].closestClass eq classOf[Array[String]])
assert(Tag[Array[List[Any]]].closestClass eq classOf[Array[List[Any]]])
object test1 {
sealed trait TX0
class TX1 extends TX0
class TX2 extends TX0
class TX3 extends TX0
type OX[T] = TX1 with TX2 with T
}
assert(!Tag[test1.OX[ZY#Y]].hasPreciseClass)
assert(!Tag[test1.OX[test1.TX3]].hasPreciseClass)
assert(!Tag[test1.OX[test1.TX0]].hasPreciseClass)
assert(Tag[test1.OX[ZY#Y]].closestClass eq classOf[Any])
assert(Tag[test1.OX[test1.TX3]].closestClass eq classOf[test1.TX0])
assert(Tag[test1.OX[test1.TX0]].closestClass eq classOf[test1.TX0])
object test2 {
type ArrayT <: Array[Byte]
}
assert(!Tag[test2.ArrayT].hasPreciseClass)
assert(Tag[test2.ArrayT].closestClass eq classOf[Array[Byte]])
}
"Work with term type prefixes" in {
val zy = new ZY {}
val zx = new ZY {}
assertSameStrict(Tag[zy.T].tag, LTT[zy.T])
assertNotChildStrict(Tag[zy.T].tag, LTT[zx.T])
assertSameStrict(Tag[zy.x.type].tag, LTT[zy.x.type])
assertChild(Tag[zy.x.type].tag, LTT[String])
assertChild(Tag[zy.x.type].tag, LTT[java.io.Serializable])
assertNotChildStrict(Tag[zy.x.type].tag, LTT[zx.x.type])
assertSameStrict(Tag[zy.y.type].tag, LTT[zy.y.type])
assertChild(Tag[zy.y.type].tag, LTT[java.lang.Object])
assertNotChildStrict(Tag[zy.y.type].tag, LTT[zx.y.type])
assertNotChildStrict(Tag[zy.y.type].tag, LTT[zx.x.type])
}
"correctly resolve abstract types inside traits when summoned inside trait" in {
val a = new ContainerDef {}
val b = new ContainerDef {}
assert(a.make.tag == Tag[DockerContainer[a.T]].tag)
assertDifferent(a.make.tag, Tag[DockerContainer[b.T]].tag)
assertSameStrict(Tag[DockerContainer[a.T]].tag, Tag[DockerContainer[a.T]].tag)
assertDifferent(Tag[DockerContainer[a.T]].tag, Tag[DockerContainer[b.T]].tag)
val zy = new ZY {}
assert(zy.tagT.getMessage contains "could not find implicit value")
assert(zy.tagU.getMessage contains "could not find implicit value")
assert(zy.tagV.getMessage contains "could not find implicit value")
assert(zy.tagA.isSuccess)
}
"combine higher-kinded type lambdas without losing ignored type arguments" in {
val tag = `LTT[_[+_,+_]]`[({ type l[F[+_, +_]] = BlockingIO3[λ[(`-R`, `+E`, `+A`) => F[E, A]]] })#l]
val res = tag.combine(`LTT[_,_]`[IO])
val tagMono = LTT[BlockingIO[IO]]
assertSameStrict(res, tagMono)
}
"resolve a higher-kinded type inside a named type lambda with ignored type arguments" in {
def mk[F[+_, +_]: TagKK] = Tag[BlockingIO3[F2To3[F, *, *, *]]]
val tag = mk[IO]
val tagMono = Tag[BlockingIO[IO]]
assertSameStrict(tag.tag, tagMono.tag)
}
"resolve TagKK from an odd higher-kinded Tag with swapped & ignored parameters (low-level)" in {
type Lt[F[_, _, _], _1, _2, _3] = F[_2, _3, _1]
val ctorTag: LightTypeTag = implicitly[Tag.auto.T[Lt]].tag
val eitherRSwapTag = LTagK3[EitherRSwap].tag
val throwableTag = LTag[Throwable].tag
val combinedTag = HKTag
.appliedTagNonPosAux(
classOf[Any],
ctor = ctorTag,
args = List(
Some(eitherRSwapTag),
Some(throwableTag),
None,
None
)
).tag
val expectedTag = TagKK[Lt[EitherRSwap, Throwable, *, *]].tag
assertSameStrict(combinedTag, expectedTag)
}
"correctly resolve a higher-kinded nested type inside a named swap type lambda" in {
def mk[F[+_, +_]: TagKK] = Tag[BIOService[SwapF2[F, *, *]]]
val tag = mk[Either]
assertSameStrict(tag.tag, Tag[BIOService[SwapF2[Either, *, *]]].tag)
assertSameStrict(tag.tag, Tag[BIOService[Swap]].tag)
assertSameStrict(tag.tag, Tag[BIOService[λ[(E, A) => Either[A, E]]]].tag)
}
"support subtyping of parents parameterized with type lambdas in combined tags" in {
val childBase = `LTT[_[_,_]]`[RoleChild]
val childArg = `LTT[_,_]`[Either]
val combinedTag = childBase.combine(childArg)
val parentTag = LTT[RoleParent[Either[Throwable, *]]]
val childTag = LTT[RoleChild[Either]]
assertChild(combinedTag, childTag)
assertSame(combinedTag, childTag)
assertChild(combinedTag, parentTag)
assertNotChild(parentTag, combinedTag)
}
"support subtyping of parents parameterized with type lambdas in combined tags with multiple parameters" in {
val childBase = `LTT[_[+_,+_],_,_]`[RoleChild2]
val childArgs = Seq(`LTT[_,_]`[Either], LTT[Int], LTT[String])
val combinedTag = childBase.combine(childArgs: _*)
val expectedTag = LTT[RoleParent[Either[Throwable, *]]]
val noncombinedTag = LTT[RoleChild2[Either, Int, String]]
assertSame(combinedTag, noncombinedTag)
assertChild(noncombinedTag, expectedTag)
assertChild(combinedTag, expectedTag)
}
"combine inside type lambdas with repeated usages of a type lambda type parameter" in {
def mk[F[+_, +_]: TagKK] = Tag[RepeatedBlockingIO[F]]
val tag = mk[IO]
val tagMono = Tag[RepeatedBlockingIO[IO]]
assertSameStrict(tag.tag, tagMono.tag)
}
"combine inside type lambdas with repeated usages of an outer type" in {
def mk[F[+_, +_]: TagKK] = Tag[RepeatedNonLambdaBlockingIO[F]]
val tag = mk[IO]
val tagMono = Tag[RepeatedNonLambdaBlockingIO[IO]]
assertSameStrict(tag.tag, tagMono.tag)
}
"combine inside type lambdas with repeated usages of an outer distinct type with the same type symbol" in {
def mk[F[+_, +_]: TagKK] = Tag[RepeatedSymbolNonLambdaBlockingIO[F]]
val tag = mk[IO]
val tagMono = Tag[RepeatedSymbolNonLambdaBlockingIO[IO]]
assertSameStrict(tag.tag, tagMono.tag)
}
"regression test: https://github.com/zio/izumi-reflect/issues/82, convert trifunctor hkt to bifunctor when combining tags" in {
def tag[F[-_, +_, +_]: TagK3] = Tag[BIO2[F[Any, +*, +*]]]
assertSameStrict(tag[ZIO].tag, Tag[BIO2[IO]].tag)
}
"combine higher-kinded types without losing ignored type arguments" in {
def mk[F[+_, +_]: TagKK] = Tag[BlockingIO[F]]
val tag = mk[IO]
val tagMono = Tag[BlockingIO[IO]]
assertSameStrict(tag.tag, tagMono.tag)
}
"resolve a higher-kinded type inside an anonymous type lambda with ignored & higher-kinded type arguments" in {
def mk[F[_[_], _]: TagTK] = Tag[BlockingIO3T[({ type l[R, E[_], A] = F[E, A] })#l]]
val tag = mk[OptionT]
assertSameStrict(tag.tag, Tag[BlockingIOT[OptionT]].tag)
}
"correctly resolve a higher-kinded nested type inside an anonymous swap type lambda" in {
def mk[F[+_, +_]: TagKK] = Tag[BIOService[λ[(E, A) => F[A, E]]]]
val tag = mk[Either]
assertSameStrict(tag.tag, Tag[BIOService[SwapF2[Either, *, *]]].tag)
assertSameStrict(tag.tag, Tag[BIOService[Swap]].tag)
assertSameStrict(tag.tag, Tag[BIOService[λ[(E, A) => Either[A, E]]]].tag)
}
"handles abstract types instead of parameters" in {
trait T1 {
type F[F0[_], A0] = OptionT[F0, A0]
type C[_, _]
type G[_]
type A
type B
def x: Tag[F[G, Either[A, B]]]
}
val t1: T1 {
type G[T] = List[T]
type C[A0, B0] = Either[A0, B0]
type A = Int
type B = Byte
} = new T1 {
type G[T] = List[T]
type C[A0, B0] = Either[A0, B0]
type A = Int
type B = Byte
// Inconsistent handling of type aliases by scalac...
// No TagK for G, but if G is inside an object or enclosing class
// then there is a TagK
val g: TagK[G] = TagK[List]
final val x: Tag[F[G, Either[A, B]]] = {
implicit val g0: TagK[G] = g
val _ = g0
Tag[F[G, C[A, B]]]
}
}
assertSameStrict(t1.x.tag, fromRuntime[OptionT[List, Either[Int, Byte]]])
}
"Generates lambda parents for lambda bases" in {
val childBase = `LTT[_[_,_]]`[RoleChild]
val fullDb = childBase.basesdb
fullDb.foreach {
case (_, parents) =>
parents.foreach {
p =>
if (p.toString.contains("RoleParent")) {
assert(p.isInstanceOf[LightTypeTagRef.Lambda])
assert(p.asInstanceOf[LightTypeTagRef.Lambda].input.size == 1)
}
}
}
}
"subtyping for Invariant Java HKT" in {
val collection = TagK[java.util.Collection]
val javaIterable = TagK[java.lang.Iterable]
assertChildStrict(collection.tag, javaIterable.tag)
}
"subtyping for Invariant Scala HKT" in {
val mutableSet = TagK[scala.collection.mutable.Set]
val collectionSet = TagK[scala.collection.Set]
assertChildStrict(mutableSet.tag, collectionSet.tag)
}
"Work for structural concrete types" in {
assertSameStrict(Tag[{ def a: Int; def g: Boolean }].tag, fromRuntime[{ def a: Int; def g: Boolean }])
assertSameStrict(Tag[Int { def a: Int }].tag, fromRuntime[Int { def a: Int }])
assertSameStrict(Tag[With[str.type] with ({ type T = str.type with Int })].tag, fromRuntime[With[str.type] with ({ type T = str.type with Int })])
assertNotChildStrict(Tag[With[str.type] with ({ type T = str.type with Int })].tag, fromRuntime[With[str.type] with ({ type T = str.type with Long })])
}
"Work for any abstract type with available Tag while preserving additional type refinement" in {
def testTag[T: Tag] = Tag[T { type X = Int; type Y = String }]
assertSameStrict(testTag[String].tag, fromRuntime[String { type X = Int; type Y = String }])
assertNotChildStrict(testTag[String].tag, fromRuntime[String { type X = String; type Y = Boolean }])
assertNotChildStrict(testTag[String].tag, fromRuntime[String { type X = String; type Y = Boolean }])
assertNotChildStrict(testTag[String].tag, fromRuntime[String { type X = Int; type Y = Boolean }])
assertNotChildStrict(testTag[String].tag, fromRuntime[String { type X = Boolean; type Y = String }])
}
"Work for any abstract type with available Tag while preserving additional method refinement" in {
def testTag[T: Tag] = Tag[T { def x: Int; val y: String }]
assertSameStrict(testTag[String].tag, fromRuntime[String { def x: Int; val y: String }])
assertNotChildStrict(testTag[String].tag, fromRuntime[String { def x: String; val y: Boolean }])
assertNotChildStrict(testTag[String].tag, fromRuntime[String { def x: Int; val y: Boolean }])
assertNotChildStrict(testTag[String].tag, fromRuntime[String { def x: Boolean; val y: String }])
}
"can resolve parameters in structural types" in {
def t[X: Tag]: Tag[{ type T = X }] = Tag[{ type T = X }]
val t1 = t[Int].tag
val t2 = Tag[{ type T = Int }].tag
val t3 = Tag[{ type T = SubStrC }].tag
assertSame(t1, t2)
assertDifferent(t1, t3)
}
"combine higher-kinded type members" in {
def combine1[X[_[_], _]: TagTK, F[_]: TagK, A: Tag]: Tag[X[F, A]] = Tag[X[F, A]]
def combine2[F[_]: TagK, A: Tag]: Tag[F[A]] = Tag[F[A]]
val t1 = TagTK[HigherKindedTypeMember.T]
val t2 = TagK[HigherKindedTypeMember.T[IO[Throwable, *], *]]
val t3 = TagTK[HigherKindedTypeMember.G]
val tres1 = combine1[HigherKindedTypeMember.T, IO[Throwable, *], Int](t1, implicitly, implicitly)
val tres2 = combine2[HigherKindedTypeMember.T[IO[Throwable, *], *], Int](t2, implicitly)
val tres3 = combine1[HigherKindedTypeMember.G, IO[Throwable, *], Int](t3, implicitly, implicitly)
assertSameStrict(tres1.tag, Tag[HigherKindedTypeMember.T[IO[Throwable, *], Int]].tag)
assertSameStrict(tres2.tag, Tag[HigherKindedTypeMember.T[IO[Throwable, *], Int]].tag)
assertSameStrict(tres3.tag, Tag[HigherKindedTypeMember.G[IO[Throwable, *], Int]].tag)
assertChildStrict(tres1.tag, Tag[AnyVal].tag)
assertChildStrict(tres2.tag, Tag[AnyVal].tag)
assertChildStrict(tres3.tag, Tag[HigherKindedTypeMember.T[IO[Throwable, *], Int]].tag)
assertChildStrict(tres3.tag, Tag[AnyVal].tag)
// unnapliedInheritanceDB contains evidence that G <: T
val t3InhBases = t3.tag.idb(tres3.tag.ref.withoutArgs.asInstanceOf[LightTypeTagRef.NameReference])
assert(t3InhBases.contains(tres1.tag.ref.withoutArgs.asInstanceOf[LightTypeTagRef.NameReference]))
}
"regression test: do not be confused by a type alias of Set of an abstract type referred via this-prefix on Scala 3" in {
val t1 = Tag[RoleDep.RoleDeps[Int, Int]].tag
val t2 = Tag[Set[RoleDep.RoleDep[Int, Int]]].tag
val t3 = LTT[RoleDep.RoleDeps[Int, Int]]
assertSameStrict(t1, t2)
assertSameStrict(t1, t3)
assertDebugSame(t1, t2)
assertDebugSame(t1, t3)
}
"support injecting runtime tags in place of type projections from type parameters / match types on type parameters" in {
def tag[TC <: DiscoverableService](implicit tag: Tag[GetDiscoveryNode[TC]]): Tag[DiscoveryNodeProvider[GetDiscoveryNode[TC]]] = {
Tag[DiscoveryNodeProvider[GetDiscoveryNode[TC]]]
}
val t1 = tag[DiscoverableServiceImpl].tag
val t2 = Tag[DiscoveryNodeProvider[NodeIdImpl]].tag
assertSameStrict(t1, t2)
assertDebugSame(t1, t2)
}
"regression test for: Scala 2, https://github.com/zio/izumi-reflect/issues/189, parameterized type alias with intersection produces incorrect output" in {
def elementTag[F[_]: TagK]: Tag[SrcContextProcessor[F]] = Tag[TestModel.x.SrcContextProcessor[F]]
assert(elementTag[CIO].tag == Tag[TestModel.x.SrcContextProcessor[CIO]].tag)
type K[F[_]] = Set[TestModel.x.SrcContextProcessor[F]]
assert(TagT[K].tag.combine(TagK[CIO].tag) == Tag[Set[TestModel.x.SrcContextProcessor[CIO]]].tag)
def aliasedTag[F[_]: TagK]: Tag[Set[SrcContextProcessor[F]]] = Tag[K[F]]
assert(aliasedTag[CIO].tag == Tag[Set[TestModel.x.SrcContextProcessor[CIO]]].tag)
def directTag[F[_]: TagK]: Tag[Set[SrcContextProcessor[F]]] = Tag[Set[TestModel.x.SrcContextProcessor[F]]]
assert(directTag[CIO].tag == Tag[Set[TestModel.x.SrcContextProcessor[CIO]]].tag)
}
"combining with wildcards is supported" in {
def tag[F[_]: TagK]: Tag[OptionT[F, _ <: Int]] = Tag[OptionT[F, _ <: Int]]
val t1 = tag[Set]
val t2 = tag[List]
val t3 = tag[* => Int]
assertSameStrict(t1.tag, Tag[OptionT[Set, _ <: Int]].tag)
assertSameStrict(t2.tag, Tag[OptionT[List, _ <: Int]].tag)
assertSameStrict(t3.tag, Tag[OptionT[* => Int, _ <: Int]].tag)
}
"other type members' bounds are not malformed when resolving parameters in structural types" in {
def t[X: Tag]: Tag[{ type G >: Int <: AnyVal; type T = X }] = Tag[{ type G >: Int <: AnyVal; type T = X }]
val t1 = t[Int].tag
val t2 = Tag[{ type G >: Int <: AnyVal; type T = Int }].tag
val t3 = Tag[{ type G >: Int <: AnyVal; type T = SubStrC }].tag
assertSame(t1, t2)
assertDifferent(t1, t3)
}
"form a correct type lambda for an equal-bounded abstract type" in {
def tag[F[_, _]: TagKK]: Tag[F[Int, String]] = Tag[F[Int, String]]
val t1 = tag[RoleDep.RoleDep]
val t2 = tag[RoleDep.RoleDeps]
assertSameStrict(t1.tag, Tag[RoleDep.RoleDep[Int, String]].tag)
assertDifferent(t1.tag, Tag[Any].tag)
assertSameStrict(t2.tag, Tag[RoleDep.RoleDeps[Int, String]].tag)
assertDifferent(t2.tag, Tag[Any].tag)
}
"eradicate intersection tautologies with Any/Object (Tag)" in {
assertSameStrict(Tag[Any with Option[String]].tag, LTT[Option[String]])
assertSameStrict(Tag[AnyRef with Option[String]].tag, LTT[Option[String]])
assertSameStrict(Tag[Object with Option[String]].tag, LTT[Option[String]])
}
"tautological intersections with Any/Object are discarded from internal structure (Tag)" in {
assertSameStrict(Tag[(Object {}) @IdAnnotation("x") with Option[(String with Object) {}]].tag, LTT[Option[String]])
assertSameStrict(Tag[(Any {}) @IdAnnotation("x") with Option[(String with Object) {}]].tag, LTT[Option[String]])
assertSameStrict(Tag[(AnyRef {}) @IdAnnotation("x") with Option[(String with Object) {}]].tag, LTT[Option[String]])
assertDebugSame(Tag[(Object {}) @IdAnnotation("x") with Option[(String with Object) {}]].tag, LTT[Option[String]])
assertDebugSame(Tag[(Any {}) @IdAnnotation("x") with Option[(String with Object) {}]].tag, LTT[Option[String]])
assertDebugSame(Tag[(AnyRef {}) @IdAnnotation("x") with Option[(String with Object) {}]].tag, LTT[Option[String]])
}
"regression test for https://github.com/zio/izumi-reflect/issues/474" in {
assertCompiles("Tag[Array[Byte]]")
}
"resolve parameters inside type bounds & wildcards" in {
def getUpperBoundTag[T: Tag]: Tag[Set[_ <: T]] = Tag[Set[_ <: T]]
def getLowerBoundTag[T: Tag]: Tag[Set[_ >: T]] = Tag[Set[_ >: T]]
assertRepr(getUpperBoundTag[Int].tag, "Set[=?: ]")
assertSame(getUpperBoundTag[Int].tag, Tag[Set[_ <: Int]].tag)
assertRepr(getLowerBoundTag[Int].tag, "Set[=?: ]")
assertSame(getLowerBoundTag[Int].tag, Tag[Set[_ >: Int]].tag)
// for recursive bound case
def x[T[_]: TagK, U: Tag]: Tag[Set[_ <: T[U]]] = Tag[Set[_ <: T[U]]]
assertRepr(x[List, Long].tag, "Set[=?: ]")
assertSameStrict(x[List, Long].tag, Tag[Set[_ <: List[Long]]].tag)
def xcontra[T[_]: TagK, U: Tag]: Tag[(_ <: T[U]) => Int] = Tag[(_ <: T[U]) => Int]
assertRepr(xcontra[List, Long].tag, "Function1[-?: ,+Int]")
assertSameStrict(xcontra[List, Long].tag, Tag[(_ <: List[Long]) => Int].tag)
}
}
}
trait SomeTrait {
type Abstract
}
object SomeObject {
type Abstract
}
// https://github.com/scala/bug/issues/11139
final case class testTag3[F[_]: TagK]() {
type X = OptionT[F, Int]
val res = Tag[X].tag
}
object X76 {
sealed trait T[A]
val x = implicitly[Tag[T[Int]]]
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/TagAssertions.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.test
import izumi.reflect.macrortti.{LTT, LTag, LightTypeTag}
import org.scalatest.wordspec.AnyWordSpec
trait TagAssertions extends AnyWordSpec with TagLogging {
def assertRepr(t: LightTypeTag, expected: String): Unit = {
assert(t.toString == expected); ()
}
def assertDebugSame(t: LightTypeTag, expected: LightTypeTag): Unit = {
val tDebug = t.debug("assert")
val expectedDebug = expected.debug("assert")
val clue = s"${t.repr} debug== ${expected.repr}"
def failClue = s"debug1: $tDebug\ndebug2: $expectedDebug"
info(clue)
assert(tDebug == expectedDebug, s"$clue\n$failClue"); ()
}
def assertSame(t: LightTypeTag, expected: LightTypeTag): Unit = {
val clue = s"${t.repr} =?= ${expected.repr}"
info(clue)
assert(t =:= expected, clue)
assert(t.ref == expected.ref, s"ref: $clue"); ()
}
def assertSameRef(t: LightTypeTag, expected: LightTypeTag): Unit = {
val clue = s"${t.repr} =?= ${expected.repr}"
info(clue)
assert(t.ref == expected.ref, s"ref: $clue"); ()
}
def assertDifferent(t: LightTypeTag, expected: LightTypeTag): Unit = {
val clue = s"${t.repr} =!= ${expected.repr}"
info(clue)
assert(!(t =:= expected), clue); ()
}
def assertChild(child: LightTypeTag, parent: LightTypeTag): Unit = {
val clue = s"${child.repr} < ${parent.repr}"
def failClue = s"1: ${child.debug()}\n2: ${parent.debug()}"
info(clue)
assert(child <:< parent, s"$clue\n$failClue"); ()
}
def assertNotChild(child: LightTypeTag, parent: LightTypeTag): Unit = {
val clue = s"${child.repr} ${combined.repr} =?= ${expected.repr}"
info(clue)
assert(combined =:= expected, clue); ()
}
def assertCombine(outer: LightTypeTag, inner: LightTypeTag, expected: LightTypeTag): Unit = {
val combined = outer.combine(inner)
val clue = s"(${outer.repr})•(${inner.repr}) => ${combined.repr} =?= ${expected.repr}"
info(clue)
assert(combined =:= expected, clue); ()
}
def assertCombineNonPos(outer: LightTypeTag, inner: Seq[Option[LightTypeTag]], expected: LightTypeTag): Unit = {
val combined = outer.combineNonPos(inner: _*)
val clue = s"(${outer.repr})•(${inner.map(_.map(_.repr)).mkString(",")}) => ${combined.repr} =?= ${expected.repr}"
info(clue)
assert(combined =:= expected, clue); ()
}
def assertIntersection(intersection: List[LightTypeTag], expected: LightTypeTag): Unit = {
val intersected = LightTypeTag.refinedType(intersection, structure = LTT[Any], additionalTypeMembers = Map.empty)
val clue = s"(${intersection.map(_.repr).mkString(" & ")}) => ${intersected.repr} =?= ${expected.repr}"
info(clue)
assert(intersected =:= expected, clue)
assertDebugSame(intersected, expected)
()
}
def assertSameStrict(t: LightTypeTag, expected: LightTypeTag): Unit = {
assertSame(t, expected)
assertChild(t, expected)
assertChild(expected, t)
}
def assertChildStrict(t: LightTypeTag, expected: LightTypeTag): Unit = {
assertChild(t, expected)
assertNotChild(expected, t)
assertDifferent(expected, t)
}
def assertNotChildStrict(t: LightTypeTag, expected: LightTypeTag): Unit = {
assertNotChild(t, expected)
assertNotChild(expected, t)
assertDifferent(expected, t)
}
def literalLtt(s: String)(implicit l: LTag[s.type]): LightTypeTag = l.tag
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/TagCombineTest.scala
================================================
package izumi.reflect.test
import izumi.reflect.macrortti.LightTypeTag
import izumi.reflect.{Tag, TagK, TagK3}
object TagCombineTest {
trait P
trait C extends P
trait HuIO[-R, +E, +I]
class Case1[T] {
final def addHas[F[-_, +_, +_]: TagK3, E: Tag, I <: T: Tag](): LightTypeTag = {
val t1 = TagK[F[Any, E, *]]
val t2 = t1.tag.combine(Tag[I].tag)
t2
}
}
}
class TagCombineTest extends TagAssertions {
import TagCombineTest._
"Tag macro" should {
"reconstruct lambda tags" in {
val expected = Tag[HuIO[Any, Int, C]].tag
val combined = TagK[HuIO[Any, Int, *]].tag.combine(Tag[C].tag)
assert(combined == expected)
val returned = new Case1[P]().addHas[HuIO, Int, C]()
assert(returned == expected)
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/TagLogging.scala
================================================
package izumi.reflect.test
import izumi.reflect.internal.fundamentals.platform.assertions.IzAssert
import izumi.reflect.internal.fundamentals.platform.console.TrivialLogger
import izumi.reflect.macrortti.LightTypeTag
import org.scalatest.wordspec.AnyWordSpec
trait TagLogging extends AnyWordSpec {
// enable subtype comparison / .fromRuntime construction logging in tests
final def withDebugOutput(f: => Any): Unit = TagLogging.withDebugOutput { f; () }
final def withSanityChecks(f: => Any): Unit = TagLogging.withSanityChecks { f; () }
final def println(o: Any): Unit = info(o.toString)
final def println(o: LightTypeTag): Unit = info(o.ref.toString)
}
object TagLogging {
def withDebugOutput[T](f: => T): T = {
synchronized {
val enabledBefore = TrivialLogger.statusLogs()
if (!enabledBefore) { TrivialLogger.enableLogs() }
try {
f
} finally {
if (!enabledBefore) { TrivialLogger.disableLogs() }
}
}
}
def withSanityChecks[T](f: => T): T = {
synchronized {
val enabledBefore = IzAssert.statusAsserts()
if (!enabledBefore) { IzAssert.enableAsserts() }
try {
f
} finally {
if (!enabledBefore) { IzAssert.disableAsserts() }
}
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/TagProgressions.scala
================================================
package izumi.reflect.test
import org.scalatest.exceptions.TestFailedException
trait TagProgressions { this: TagAssertions =>
final def brokenOnScala3(f: => Any): Unit = {
if (IsScala3) broken(f) else f; ()
}
final def brokenOnScala2(f: => Any): Unit = {
if (!IsScala3) broken(f) else f; ()
}
final def brokenOnScala2MinorVersion(versions: Int*)(f: => Any): Unit = {
if (versions.contains(Scala2MinorVersion.scala2MinorVersion)) broken(f) else f; ()
}
final def broken(f: => Any): Unit = {
intercept[TestFailedException](f); ()
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/TestModel.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.test
import izumi.reflect.Tag
import scala.annotation.StaticAnnotation
import izumi.reflect.macrortti.{LTag, LightTypeTag}
object TestModel {
type BlockingIO[F[_, _]] = BlockingIO3[λ[(R, E, A) => F[E, A]]]
type BIO2[F[+_, +_]] = BIO3[λ[(`-R`, `+E`, `+A`) => F[E, A]]]
final class With[T] extends StaticAnnotation
final class IdAnnotation(val name: String) extends StaticAnnotation
trait YieldOpCounts {
def zioYieldOpCount: Int = 1024
def blockingYieldOpCount: Int = Int.MaxValue
}
object YieldOpCounts extends YieldOpCounts
trait T0[A[_], B[_]]
final val str = "str"
type Id[T] = T
type FP1[+T] = List[T]
type Ap1[+F[+_], +T] = F[T]
type FP[+T] = FP1[T]
type L[P] = List[P]
type LN[P <: Number] = List[P]
trait T1[U[_]]
type FI[IGNORE] = Unit
trait T2[U[_[_], _[_]]]
// type K = T1[F]
// val a: K = new T1[F] {}
trait C {
type A
}
trait R0[K, A <: R0[K, A]]
trait R1[K] extends R0[K, R1[K]]
type S[A, B] = Either[B, A]
trait W1
trait W2 extends W1
trait W3[X]
trait W4[A] extends W3[A]
trait W5[B] extends W2
type T3[A, B] = W5[A] with W4[B] with W1
trait I1
trait I2 extends I1
trait F1[+A]
trait F2[+A] extends F1[A]
trait F3 extends F2[Int]
trait FT1[+A[+_[+_]]]
trait FT2[+A[+_[+_]]] extends FT1[A]
trait IT1[+K[+_]]
trait IT2[+K[+_]] extends IT1[K]
trait FM1[+A, +B]
trait FM2[+A] extends FM1[A, Unit]
type NestedTL[G[_, _], A, B] = FM2[G[A, (B, A)]]
type NestedTL2[A, B, G[_]] = FM2[G[S[B, A]]]
type Const[A, B] = B
type XS <: { type X }
type WithX = { type X }
type FXS <: { type F[A] = A }
trait H1
trait H2 extends H1
trait H3 extends H2
trait H4 extends H3
trait H5 extends H4
trait J1[F[_]]
trait J2
trait J3
trait J[F[_]] extends J1[F] with J2 with J3
object TPrefix {
type T
}
trait P0[A[_], B[_]]
trait P1[A[_], B[_]] extends P0[B, A]
trait X1[x]
trait X2[x]
trait XP1[A[_]] extends P0[X2, A]
trait RoleParent[F[_]]
trait RoleChild[F[_, _]] extends RoleParent[F[Throwable, *]]
class RoleChild2[F[+_, +_], A, B] extends RoleParent[F[Throwable, *]]
class ApplePaymentProvider[F[_]] extends H1
trait ZIO[-R, +E, +A]
type IO[+E, +A] = ZIO[Any, E, A]
class BlockingIO3[F[_, _, _]]
object PDTNormA {
trait Service
}
val PDTNormB = PDTNormA
trait KT1[+A1, +B1]
trait KT2[+A2, +B2] extends KT1[B2, A2]
trait KK1[+A, +B, +U]
trait KK2[+A, +B] extends KK1[B, A, Unit]
class \/[L, R](implicit val lt: LTag[L], val rt: LTag[R])
type SubStrA <: String
type SubStrB <: String
type SubStrC = String
type SubStrD = SubStrA
type SubSubStr <: SubStrA
final case class VarArgsAnyVal(args: String*) extends AnyVal
trait BIO3[F[-_, +_, +_]]
trait CIO[+A]
object x {
type SrcContextProcessor[F[_]] = SrcProcessor with ContextProcessor[F]
}
trait SrcProcessor
trait ContextProcessor[F[_]]
trait IntegrationCheck[+F[_]]
object ThisPrefixTest {
class ThisPrefix {
val tag: LightTypeTag = Tag[this.type].tag
}
object ThisPrefix {
val tag: LightTypeTag = Tag[this.type].tag
}
}
object BasicCases {
object BasicCase2 {
class TestImpl0Good
}
}
object HigherKindedTypeMember {
type T[F[_], A] >: Unit <: AnyVal
type G[F[_], A] >: Unit <: T[F, A]
}
object RoleDep {
type RoleDep[RoleId, C] >: Any
type RoleDeps[RoleId, C] = Set[this.RoleDep[RoleId, C]] // this-prefix is important, do not remove
}
trait Trait1 {
def dep: Any
}
trait Trait3[T] extends Trait1 {
def dep: T
}
trait Trait4
trait AbstractRole[F[_]]
class TargetRole extends AbstractRole[Id]
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/BasicDottyTestMirror.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.test
class BasicDottyTestMirror extends CurrentDottySupportExtentTest
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/DiscoveryModel.scala
================================================
package izumi.reflect.test
object DiscoveryModel {
trait NodeId
trait DiscoverableService {
type DiscoveryNode <: NodeId
}
trait DiscoveryNodeProvider[Id <: NodeId]
trait DiscoverableServiceImpl extends DiscoverableService {
override type DiscoveryNode = NodeIdImpl
}
class NodeIdImpl extends NodeId
type GetDiscoveryNode[T <: DiscoverableService] = T#DiscoveryNode
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/IsScala3.scala
================================================
package izumi.reflect.test
import izumi.reflect.internal.fundamentals.platform.language.unused
import scala.language.implicitConversions
object IsScala3 {
implicit def IsScala3(@unused t: this.type): Boolean = false
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/LightTypeTagProgressionTest.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.test
class LightTypeTagProgressionTest extends SharedLightTypeTagProgressionTest {
// "[progression] lightweight type tags (Scala 2)" should {
//
// }
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/LightTypeTagTest.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.test
import izumi.reflect.macrortti._
class LightTypeTagTest extends SharedLightTypeTagTest {
import TestModel._
"lightweight type tags (Scala 2)" should {
"support structural & refinement type equality (Scala 2 specific, generic type projection from an abstract type member)" in {
val a1 = new C {
override type A = Int
}
object Z {
type X <: { type A = Int }
}
val _ = (a1, Z)
assertSame(LTT[a1.A], LTT[Z.X#A])
}
"strong summons test (Scala 2 specific, generic type projection from an abstract type member)" in {
assertCompiles("def x1 = { object x { type T <: { type Array } }; LTag[x.T#Array]; () }")
}
"there should be no unexpected lambdas in bases db produced from nested existential types (Scala 2 specific, unreducible application of higher-kinded type to wildcard arguments) (regression test https://github.com/zio/izumi-reflect/issues/345)" in {
trait L[ARRG0]
trait Test0[+ARRG1]
trait Test1[+ARRG2] extends Test0[ARRG2]
type T1[AAA] = Test1[L[AAA]]
val list_ = LTT[T1[_]]
info(list_.debug())
assert(!list_.debug().contains("→ izumi.reflect.test.LightTypeTagProgressionTest.Test0[+izumi.reflect.test.LightTypeTagProgressionTest.L[=?]]"))
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/TagProgressionTest.scala
================================================
package izumi.reflect.test
class TagProgressionTest extends SharedTagProgressionTest {
// All Scala 2 specific progression tests have been fixed and moved to TagTest
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/TagTest.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.test
import izumi.reflect._
import izumi.reflect.macrortti._
import org.scalatest.exceptions.TestFailedException
class TagTest extends SharedTagTest {
import izumi.reflect.test.PlatformSpecific.fromRuntime
override final val tagZ = Tag[String]
trait T2[A, B, C[_[_], _], D[_], E]
trait Trait1 {
def dep: Dep
}
trait Trait3[T <: Dep] extends Trait1 {
def dep: T
}
"Tag (Scala 2)" should {
"Work for an abstract type with available TagK when TagK is requested through an explicit implicit (Scala 2 HKTag Syntax)" in {
def testTagK[F[_], T: Tag](implicit ev: HKTag[{ type Arg[C] = F[C] }]) = {
val _ = ev
Tag[F[T {}] {}]
}
assert(testTagK[Set, Int].tag == fromRuntime[Set[Int]])
}
"Handle Tags outside of a predefined set (Scala 2 HKTag Syntax)" in {
type TagX[T[_, _, _[_[_], _], _[_], _]] = HKTag[{ type Arg[A, B, C[_[_], _], D[_], E] = T[A, B, C, D, E] }]
def testTagX[F[_, _, _[_[_], _], _[_], _]: TagX, A: Tag, B: Tag, C[_[_], _]: TagTK, D[_]: TagK, E: Tag] = Tag[F[A, B, C, D, E]]
val value = testTagX[T2, Int, String, OptionT, List, Boolean]
assert(value.tag == fromRuntime[T2[Int, String, OptionT, List, Boolean]])
}
"Can create custom type tags to support bounded generics, e.g. <: Dep in TagK (Scala 2 HKTag Syntax)" in {
type `TagK<:Dep`[K[_ <: Dep]] = HKTag[{ type Arg[A <: Dep] = K[A] }]
implicitly[`TagK<:Dep`[Trait3]].tag.withoutArgs =:= LTag[Trait3[Nothing]].tag.withoutArgs
}
"support HKTag for unapplied type lambdas with type bounds" in {
type `TagK<:Dep`[K[_ <: Dep]] = HKTag[{ type Arg[A <: Dep] = K[A] }]
def t[T[_ <: Dep]: `TagK<:Dep`, A <: Dep: Tag] = Tag[T[A]]
assert(t[Trait3, Dep].tag == Tag[Trait3[Dep]].tag)
}
"support HKTag for unapplied type lambdas with higher-kinded type bounds" in {
class Trait4[K] extends Dep
class Trait5[T[_]]
type `TagKT<:Dep`[K[X[_] <: Dep]] = HKTag[{ type Arg[A[_] <: Dep] = K[A] }]
def t[T[_[_] <: Dep]: `TagKT<:Dep`, A[_] <: Dep: TagK] = Tag[T[A]]
assert(t[Trait5, Trait4].tag == Tag[Trait5[Trait4]].tag)
}
"support HKTag for higher-kinded type lambdas with interdependent inner and outer type bounds" in {
class Dep0
trait Trait30[T <: Dep0]
trait TraitK30[T[x <: Dep0] <: Trait30[x]]
type `TagK<:Dep0`[K[_ <: Dep0]] = HKTag[{ type Arg[A <: Dep0] = K[A] }]
type `TagKT<:Dep0`[K[T[x <: Dep0] <: Trait30[x]]] = HKTag[{ type Arg[T[x <: Dep0] <: Trait30[x]] = K[T] }]
def t[K[F[x <: Dep0] <: Trait30[x]]: `TagKT<:Dep0`, T[x <: Dep0] <: Trait30[x]: `TagK<:Dep0`] = Tag[K[T]]
assert(t[TraitK30, Trait30].tag == Tag[TraitK30[Trait30]].tag)
}
"example in 3.0.9 release notes works" in {
def printColType[F[+x] <: Iterable[x]: Tag.auto.T]: LightTypeTag = {
Tag[F[Int]].tag
}
assert(printColType[List] == LTT[List[Int]])
}
"can find HKTag when obscured by type lambda (Scala 2 HKTag Syntax)" in {
assertCompiles("HKTag.hktagFromTagMacro[{ type Arg[C] = Option[C] }]")
assertCompiles("HKTag.hktagFromTagMacro[({ type l[F[_]] = { type Arg[C] = F[C] } })#l[Option]]")
}
"we no longer accidentally materialize tags for type parameters that are prefixes of type projections (Scala 2 specific, generic type projection)" in {
class Path {
type Child
}
val path = new Path
// A has no tag and the definition of `getTag` should not compile at all. It's a bug that it compiles
val t = intercept[TestFailedException](
assertCompiles(
"""
def getTag[A <: Path]: Tag[A#Child] = Tag[A#Child]
"""
)
)
assert(
t.getMessage.contains("could not find implicit value") ||
t.getMessage.contains("no implicit argument of type") /*Dotty*/
)
def getTag[A <: Path](implicit t: Tag[A#Child]): Tag[A#Child] = Tag[A#Child]
val directChildTag = Tag[Path#Child].tag // Path::Child
val indirectChildTag = getTag[path.type].tag // A|::Child
assertDifferent(indirectChildTag, directChildTag)
assertNotChild(directChildTag, indirectChildTag)
assertNotChild(indirectChildTag, directChildTag)
assertRepr(indirectChildTag, "path::Child")
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-2.11/izumi/reflect/test/Scala2MinorVersion.scala
================================================
package izumi.reflect.test
object Scala2MinorVersion {
def scala2MinorVersion: Int = 11
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-2.12/izumi/reflect/test/Scala2MinorVersion.scala
================================================
package izumi.reflect.test
object Scala2MinorVersion {
def scala2MinorVersion: Int = 12
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-2.13/izumi/reflect/test/Scala2MinorVersion.scala
================================================
package izumi.reflect.test
object Scala2MinorVersion {
def scala2MinorVersion: Int = 13
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-2.13+/izumi/reflect/test/Scala213Plus_LightTypeTagTest.scala
================================================
package izumi.reflect.test
import org.scalatest.wordspec.AnyWordSpec
import izumi.reflect.macrortti.LTT
class Scala213Plus_LightTypeTagTest extends AnyWordSpec with TagAssertions with TagProgressions {
"lightweight type tags (2.13+)" should {
"literal types behave in sane manner https://github.com/zio/izumi-reflect/issues/284" in {
import izumi.reflect._
val v: "a" = "a"
type vt = v.type
object xa {
final val x = v
final val y = x
}
assertSameStrict(Tag["a"].tag, Tag[vt].tag)
assertSameStrict(Tag[xa.x.type].tag, Tag["a"].tag)
assertSameStrict(Tag[xa.y.type].tag, Tag["a"].tag)
assertDebugSame(Tag["a"].tag, Tag[vt].tag)
assertDebugSame(Tag[xa.x.type].tag, Tag["a"].tag)
assertDebugSame(Tag[xa.y.type].tag, Tag["a"].tag)
assertChildStrict(Tag["a"].tag, Tag[String].tag)
assertChildStrict(Tag[vt].tag, Tag[String].tag)
assertChildStrict(Tag[xa.x.type].tag, Tag[String].tag)
assertChildStrict(Tag[xa.y.type].tag, Tag[String].tag)
}
"support string constant types (Scala 2.13+ syntax)" in {
assertDifferent(LTT["abc"], LTT[String])
assertDifferent(LTT["abc"], LTT["cba"])
assertSameStrict(LTT["abc"], LTT["abc"])
assertChildStrict(LTT["abc"], LTT[String])
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-3/izumi/reflect/test/BasicDottyTest.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.test
class BasicDottyTest extends CurrentDottySupportExtentTest
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-3/izumi/reflect/test/DiscoveryModel.scala
================================================
package izumi.reflect.test
object DiscoveryModel {
trait NodeId
trait DiscoverableService {
type DiscoveryNode <: NodeId
}
trait DiscoveryNodeProvider[Id <: NodeId]
trait DiscoverableServiceImpl extends DiscoverableService {
override type DiscoveryNode = NodeIdImpl
}
class NodeIdImpl extends NodeId
type GetDiscoveryNode[T <: DiscoverableService] <: NodeId = T match {
case GetDiscoveryNodePattern[t] => t
}
type GetDiscoveryNodePattern[Id <: NodeId] = DiscoverableService { type DiscoveryNode = Id }
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-3/izumi/reflect/test/DottyRegressionTests.scala
================================================
package izumi.reflect.test
import izumi.reflect.Tag
object DottyRegressionTests {
// should compile
// https://github.com/zio/izumi-reflect/issues/135#issue-801046733
import zio._
trait DgraphClient
object Example extends scala.App {
type DgClient = Has[DgClient.Service]
object DgClient {
trait Service {
val dgraphClient: UIO[DgraphClient]
}
val getDgClient =
ZIO.accessM[DgClient](_.get.dgraphClient)
}
}
object zio {
type UIO[+A] = ZIO[Any, Nothing, A]
sealed trait ZIO[-R, +E, +A]
object ZIO {
def accessM[R]: AccessMPartiallyApplied[R] = new AccessMPartiallyApplied[R]()
final class AccessMPartiallyApplied[R](private val dummy: Boolean = false) extends AnyVal {
def apply[E, A](f: R => ZIO[R, E, A]): ZIO[R, E, A] = ???
}
}
trait Has[A]
object Has {
implicit final class HasSyntax[Self <: Has[_]](private val self: Self) extends AnyVal {
def get[B](implicit ev: Self <:< Has[B], tagged: Tag[B]): B = ???
}
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-3/izumi/reflect/test/IsScala3.scala
================================================
package izumi.reflect.test
inline val IsScala3 = true
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-3/izumi/reflect/test/LightTypeTagProgressionTest.scala
================================================
package izumi.reflect.test
import izumi.reflect.macrortti._
class LightTypeTagProgressionTest extends SharedLightTypeTagProgressionTest {
"[progression] lightweight type tag (Dotty)" should {
"fails to support variance for type parameters of opaque types" in {
object x {
opaque type T[+X] = X
}
broken {
assertChildStrict(LTT[x.T[Int]], LTT[x.T[AnyVal]])
}
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-3/izumi/reflect/test/LightTypeTagTest.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.test
import izumi.reflect.Tag
import izumi.reflect.macrortti.*
import izumi.reflect.macrortti.LightTypeTagRef.{AbstractReference, AppliedNamedReference, Boundaries, Lambda}
import scala.collection.immutable.ListSet
import scala.collection.{BitSet, immutable, mutable}
class LightTypeTagTest extends SharedLightTypeTagTest {
import TestModel._
"lightweight type tags (Dotty)" should {
"tautological intersections with Matchable are discarded from internal structure (Scala 3 specific, Matchable)" in {
assertSameStrict(LTT[Matchable with Option[String]], LTT[Option[String]])
assertDebugSame(LTT[Matchable with Option[String]], LTT[Option[String]])
}
"tautological intersections with Matchable are discarded from internal structure (Scala 3 specific, Matchable) (Tag)" in {
assertSameStrict(Tag[Matchable with Option[String]].tag, LTT[Option[String]])
assertDebugSame(Tag[Matchable with Option[String]].tag, LTT[Option[String]])
}
"tautological unions with Any/AnyRef/Matchable/Object are discarded from internal structure (Scala 3 specific, Matchable)" in {
assertSameStrict(LTT[Any | Matchable | AnyRef | Object | Option[String] | Nothing], LTT[Any])
assertDebugSame(LTT[Any | Matchable | AnyRef | Object | Option[String] | Nothing], LTT[Any])
assertSameStrict(LTT[Matchable | AnyRef | Object | Option[String] | Nothing], LTT[Matchable])
assertDebugSame(LTT[Matchable | AnyRef | Object | Option[String] | Nothing], LTT[Matchable])
assertSameStrict(LTT[AnyRef | Object | Option[String] | Nothing], LTT[AnyRef])
assertDebugSame(LTT[AnyRef | Object | Option[String] | Nothing], LTT[AnyRef])
assertSameStrict(LTT[Object | Option[String] | Nothing], LTT[Object])
assertDebugSame(LTT[Object | Option[String] | Nothing], LTT[Object])
assertSameStrict(LTT[Option[String] | Nothing], LTT[Option[String]])
assertDebugSame(LTT[Option[String] | Nothing], LTT[Option[String]])
}
"tautological unions with Any/AnyRef/Matchable/Object are discarded from internal structure (Scala 3 specific, Matchable) (Tag)" in {
assertSameStrict(Tag[Any | Matchable | AnyRef | Object | Option[String] | Nothing].tag, LTT[Any])
assertDebugSame(Tag[Any | Matchable | AnyRef | Object | Option[String] | Nothing].tag, LTT[Any])
assertSameStrict(Tag[Matchable | AnyRef | Object | Option[String] | Nothing].tag, LTT[Matchable])
assertDebugSame(Tag[Matchable | AnyRef | Object | Option[String] | Nothing].tag, LTT[Matchable])
assertSameStrict(Tag[AnyRef | Object | Option[String] | Nothing].tag, LTT[AnyRef])
assertDebugSame(Tag[AnyRef | Object | Option[String] | Nothing].tag, LTT[AnyRef])
assertSameStrict(Tag[Object | Option[String] | Nothing].tag, LTT[Object])
assertDebugSame(Tag[Object | Option[String] | Nothing].tag, LTT[Object])
assertSameStrict(Tag[Option[String] | Nothing].tag, LTT[Option[String]])
assertDebugSame(Tag[Option[String] | Nothing].tag, LTT[Option[String]])
}
"support top-level abstract types (Scala 3 specific, top level type aliases)" in {
assertChildStrict(LTT[LightTypeTagTestT], LTT[String])
}
"support opaque types" in {
object x {
type T >: List[Int] <: List[Int]
opaque type Opaque = List[Int]
opaque type OpaqueSub <: List[Int] = List[Int]
}
assertNotChildStrict(LTT[x.Opaque], LTT[List[Int]])
assertNotChildStrict(LTT[x.Opaque], LTT[Seq[Int]])
assertNotChildStrict(LTT[x.Opaque], LTT[x.T])
assertNotChildStrict(LTT[x.Opaque], LTT[x.OpaqueSub])
assertChildStrict(LTT[x.OpaqueSub], LTT[List[Int]])
assertChildStrict(LTT[x.OpaqueSub], LTT[Seq[Int]])
assertChildStrict(LTT[x.T], LTT[Seq[Int]])
assertChildStrict(LTT[x.OpaqueSub], LTT[x.T])
assertDifferent(LTT[x.OpaqueSub], LTT[x.T])
}
"basic support for polymorphic function types" in {
val t1 = LTT[[A] => A => A]
val t2 = LTT[[B] => B => B]
assertSameStrict(t1, t2)
}
}
}
type LightTypeTagTestT <: String
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-3/izumi/reflect/test/PlatformSpecific.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.test
import izumi.reflect.dottyreflection.Inspect
import izumi.reflect.macrortti.LightTypeTag
object PlatformSpecific {
inline def fromRuntime[T]: LightTypeTag = Inspect.inspect[T]
inline def fromRuntime[T](loud: Boolean): LightTypeTag = Inspect.inspect[T]
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-3/izumi/reflect/test/Scala2MinorVersion.scala
================================================
package izumi.reflect.test
object Scala2MinorVersion {
def scala2MinorVersion: Int = -1
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-3/izumi/reflect/test/TagProgressionTest.scala
================================================
package izumi.reflect.test
import izumi.reflect.Tag
import izumi.reflect.test.TestModel.*
class TagProgressionTest extends SharedTagProgressionTest {
"[progression] Tag (Dotty)" should {
// Can't fix this atm because simplification happens even without .simplified call because of https://github.com/lampepfl/dotty/issues/17544
"progression test: fails to don't lose tautological union components other than Nothing" in {
def tag1[T: Tag]: Tag[T | Trait1] = Tag[T | Trait1]
def tag4[T: Tag]: Tag[T | Trait4] = Tag[T | Trait4]
val t1 = tag1[Trait3[Dep]].tag
val t2 = tag4[Trait3[Dep]].tag
val t10 = Tag[Trait3[Dep] | Trait1].tag
val t20 = Tag[Trait3[Dep] | Trait4].tag
brokenOnScala3 {
assertSameStrict(t1, t10)
assertDebugSame(t1, t10)
}
assertSameStrict(t2, t20)
assertDebugSame(t2, t20)
}
// We don't really want to fix it, because removing tautologies is quadratic, with two subtyping comparisions per step!
// Would make construction really expensive, all for an extremely rare corner case
"progression test: union tautologies are not removed automatically when constructing combined union type" in {
def tag1[T: Tag]: Tag[T | Trait1] = Tag[T | Trait1]
val t1 = tag1[Trait3[Dep]].tag
val t10 = t1.removeUnionTautologies
brokenOnScala3 {
assertSameStrict(t1, t10)
assertDebugSame(t1, t10)
}
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect/src/test/scala-3/izumi/reflect/test/TagTest.scala
================================================
package izumi.reflect.test
import izumi.reflect.macrortti.{LTT, LTag}
import izumi.reflect.{HKTag, Tag, TagK, TagTK}
import izumi.reflect.test.ID.*
import izumi.reflect.test.PlatformSpecific.fromRuntime
import izumi.reflect.test.TestModel.{Trait1, Trait3, Trait4}
import org.scalatest.exceptions.TestFailedException
class TagTest extends SharedTagTest with TagAssertions {
override final val tagZ = Tag[String]
trait Trait1 {
def dep: Dep
}
trait Trait3[T <: Dep] extends Trait1 {
def dep: T
}
"Tag (Dotty)" should {
"Support union subtyping (Scala 3 specific, union types)" in {
trait Animal
trait Dog extends Animal
assertChild(Tag[Dog].tag, Tag[Animal].tag)
assertChild(Tag[Dog | String].tag, Tag[Animal | String].tag)
assertNotChild(Tag[Animal | String].tag, Tag[Dog | String].tag)
}
"Can create custom type tags to support bounded generics, e.g. <: Dep in TagK (Scala 3 HKTag Syntax)" in {
type `TagK<:Dep`[K[_ <: Dep]] = Tag[K]
implicitly[`TagK<:Dep`[Trait3]].tag.withoutArgs =:= LTag[Trait3[Nothing]].tag.withoutArgs
}
"combine union types (Scala 3 specific, union types)" in {
def t1[A: Tag] = Tag[String | A]
def t2[A: Tag, B: Tag] = Tag[A | B]
assertSameStrict(t1[Int].tag, Tag[Int | String].tag)
assertSameStrict(t2[Int, String].tag, Tag[String | Int].tag)
assertSameStrict(t1[String].tag, Tag[String].tag)
assertSameStrict(t2[String, String].tag, Tag[String].tag)
}
"type tags with bounds are successfully requested by TagMacro" in {
type `TagK<:Dep`[K[_ <: Dep]] = Tag.auto.T[K]
def t[T[_ <: Dep]: `TagK<:Dep`, A <: Dep: Tag] = Tag[T[A]]
assertSameStrict(t[Trait3, Dep].tag, Tag[Trait3[Dep]].tag)
}
"remove tautological unions with Nothing (LTT)" in {
assertSameStrict(LTT[Nothing | Option[String]], LTT[Option[String]])
}
"remove tautological unions with Nothing (Tag)" in {
assertSameStrict(Tag[Nothing | Option[String]].tag, LTT[Option[String]])
}
"combine intersection path-dependent intersection types with inner tags works on Scala 3" in {
trait PDT {
type T
implicit def tag: Tag[T]
def badCombine(that: PDT): Tag[T with that.T] = {
Tag[T with that.T]
}
def goodCombine(that: PDT): Tag[T with that.T] = {
import that.tag
Tag[T with that.T]
}
}
def PDT[U: Tag]: PDT = new PDT { type T = U; override val tag: Tag[U] = Tag[U] }
val badCombine = PDT[Int].badCombine(PDT[Unit])
assertSameStrict(badCombine.tag, Tag[Int with Unit].tag)
val goodCombine = PDT[Int].goodCombine(PDT[Unit])
assertSameStrict(goodCombine.tag, Tag[Int with Unit].tag)
}
}
}
trait Layer[A] {
def tag: Tag[A]
}
object Layer {
def succeed[A: Tag](value: => A): Layer[A] =
new Layer[A] {
override def tag: Tag[A] = implicitly
}
}
// should compile
// Example from here: https://github.com/zio/zio/issues/6071
object CachedRefinedTypeExample {
trait Animal
trait Dog extends Animal
trait Service {
def animal: Animal
}
val layer =
Layer.succeed {
new Service {
def animal: Dog = new Dog {}
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/.js/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/ReferenceEquality.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
private[boopickle] object ReferenceEquality {
@inline def eq(a: AnyRef, b: AnyRef): Boolean = a eq b
@inline def ne(a: AnyRef, b: AnyRef): Boolean = a ne b
@inline def identityHashCode(obj: AnyRef): Int = System.identityHashCode(obj)
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/.js/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/StringCodec.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
import scala.scalajs.js
import scala.scalajs.js.annotation.JSGlobal
import scala.scalajs.js.typedarray.TypedArrayBufferOps._
import scala.scalajs.js.typedarray._
/**
* This is a fork-point from original `boopickle` codebase.
*
* LightTypeTags are encoded at compile-time and decoded at run-time,
* but Scala does not use the JVM version of the artifact to run macros
* from. Instead it tries to run the Scala.js version compiled for JVM,
* but the optimized encoder in boopickle is using native JavaScript classes
* so it can't work.
*
* Here we include both the JVM and JS versions of StringCodec and use the JVM
* version for encode* methods and JS version for decode* methods.
*/
private[reflect] object StringCodec extends StringCodecBase {
@inline override def decodeFast(len: Int, buf: ByteBuffer): String = {
JSStringCodec.decodeFast(len, buf)
}
@inline override def decodeUTF8(len: Int, buf: ByteBuffer): String = {
JSStringCodec.decodeUTF8(len, buf)
}
@inline override def decodeUTF16(len: Int, buf: ByteBuffer): String = {
JSStringCodec.decodeUTF16(len, buf)
}
@inline override def encodeUTF8(str: String): ByteBuffer = {
JVMStringCodec.encodeUTF8(str)
}
@inline override def encodeUTF16(str: String): ByteBuffer = {
JVMStringCodec.encodeUTF16(str)
}
}
/**
* This is a fork-point from original `boopickle` codebase.
*
* ByteBufferProvider is only used for encoding. JVM encoder is incompatible with `DirectByteBufferProvider`,
* so use `HeapByteBufferProvider`
*/
private[reflect] object DefaultByteBufferProvider extends DefaultByteBufferProviderFuncs {
// override def provider = new DirectByteBufferProvider
override def provider = new HeapByteBufferProvider
}
private[boopickle] object JVMStringCodec extends StringCodecBase {
override def decodeUTF8(len: Int, buf: ByteBuffer): String = {
val a = new Array[Byte](len)
buf.get(a)
new String(a, StandardCharsets.UTF_8)
}
override def encodeUTF8(str: String): ByteBuffer = {
ByteBuffer.wrap(str.getBytes(StandardCharsets.UTF_8))
}
override def decodeUTF16(len: Int, buf: ByteBuffer): String = {
val a = new Array[Byte](len)
buf.get(a)
new String(a, StandardCharsets.UTF_16LE)
}
override def encodeUTF16(str: String): ByteBuffer = {
ByteBuffer.wrap(str.getBytes(StandardCharsets.UTF_16LE))
}
}
/**
* Facade for native JS engine provided TextDecoder
*/
@js.native
@JSGlobal
private[boopickle] class TextDecoder(utfLabel: js.UndefOr[String] = js.undefined) extends js.Object {
def decode(data: ArrayBufferView): String = js.native
}
/**
* Facade for native JS engine provided TextEncoder
*/
@js.native
@JSGlobal
private[boopickle] class TextEncoder(utfLabel: js.UndefOr[String] = js.undefined) extends js.Object {
def encode(str: String): Int8Array = js.native
}
private[boopickle] object JSStringCodec extends StringCodecBase {
private lazy val utf8decoder: (Int8Array) => String = {
val td = new TextDecoder
// use native TextDecoder
(data: Int8Array) => td.decode(data)
}
private lazy val utf8encoder: (String) => Int8Array = {
val te = new TextEncoder
// use native TextEncoder
(str: String) => new Int8Array(te.encode(str))
}
private lazy val utf16decoder: (Uint16Array) => String = {
/*
try {
// do not use native TextDecoder as it's slow
val td = new TextDecoder("utf-16none")
(data: Uint16Array) => td.decode(data)
} catch {
case e: Throwable =>
*/
(data: Uint16Array) =>
js.Dynamic.global.String.fromCharCode.applyDynamic("apply")(null, data).asInstanceOf[String]
}
private lazy val utf16encoder: (String) => Int8Array = {
/*
try {
// do not use native TextEncoder as it's slow
val te = new TextEncoder("utf-16none")
(str: String) => te.encode(str)
} catch {
case e: Throwable =>
}
*/
(str: String) =>
{
val ta = new Uint16Array(str.length)
var i = 0
while (i < str.length) {
ta(i) = str.charAt(i).toInt
i += 1
}
new Int8Array(ta.buffer)
}
}
override def decodeUTF8(len: Int, buf: ByteBuffer): String = {
if (buf.isDirect && !js.isUndefined(js.Dynamic.global.TextDecoder)) {
// get the underlying Int8Array
val ta = buf.typedArray()
val s = utf8decoder(ta.subarray(buf.position(), buf.position() + len))
(buf: java.nio.Buffer).position(buf.position() + len)
s
} else {
val a = new Array[Byte](len)
buf.get(a)
new String(a, StandardCharsets.UTF_8)
}
}
override def encodeUTF8(str: String): ByteBuffer = {
if (js.isUndefined(js.Dynamic.global.TextEncoder)) {
ByteBuffer.wrap(str.getBytes(StandardCharsets.UTF_8))
} else {
TypedArrayBuffer.wrap(utf8encoder(str))
}
}
override def decodeUTF16(len: Int, buf: ByteBuffer): String = {
if (buf.isDirect) {
val ta = new Uint16Array(buf.typedArray().buffer, buf.position() + buf.typedArray().byteOffset, len / 2)
(buf: java.nio.Buffer).position(buf.position() + len)
utf16decoder(ta)
// new String(ta.toArray) // alt implementation
} else {
val a = new Array[Byte](len)
buf.get(a)
new String(a, StandardCharsets.UTF_16LE)
}
}
override def encodeUTF16(str: String): ByteBuffer = {
TypedArrayBuffer.wrap(utf16encoder(str))
}
override def decodeFast(len: Int, buf: ByteBuffer): String = {
if (buf.hasArray)
decodeFastArray(len, buf)
else
decodeFastTypedArray(len, buf)
}
override def encodeFast(s: String, bb: ByteBuffer): Unit = {
if (bb.hasArray)
encodeFastArray(s, bb)
else
encodeFastTypedArray(s, bb)
}
protected def encodeFastTypedArray(s: String, bb: ByteBuffer): Unit = {
val len = s.length()
val buf = bb.typedArray()
var dst = bb.position()
var src = 0
var c: Char = ' '
// start by encoding ASCII only
while ((src < len) && { c = s.charAt(src); c < 0x80 }) {
buf(dst) = c.toByte
src += 1
dst += 1
}
// next stage, encode also non-ASCII
while (src < len) {
c = s.charAt(src)
if (c < 0x80) {
buf(dst) = c.toByte
dst += 1
} else if (c < 0x4000) {
buf(dst) = (0x80 | (c & 0x3F)).toByte
buf(dst + 1) = (c >> 6 & 0xFF).toByte
dst += 2
} else {
buf(dst) = (0xC0 | (c & 0x3F)).toByte
buf(dst + 1) = (c >> 6 & 0xFF).toByte
buf(dst + 2) = (c >> 14).toByte
dst += 3
}
src += 1
}
(bb: java.nio.Buffer).position(dst)
}
private def charArray2String(chars: js.Array[Int], offset: Int, len: Int): String = {
// for some reason, on Chrome, calling `cp.jsSlice` makes `fromCharCode` 2-3x faster!
js.Dynamic.global.String.fromCharCode
.applyDynamic("apply")(null, chars.jsSlice(offset, offset + len))
.asInstanceOf[String]
}
protected def decodeFastTypedArray(len: Int, buf: ByteBuffer): String = {
val cp = new js.Array[Int](len)
val src = buf.typedArray()
var offset = buf.position()
var dst = 0
while (dst < len) {
val b = src(offset) & 0xFF
offset += 1
if ((b & 0x80) == 0) {
cp(dst) = b
} else if ((b & 0xC0) == 0x80) {
val b1 = src(offset) & 0xFF
offset += 1
cp(dst) = b & 0x3F | b1 << 6
} else {
val b1 = src(offset) & 0xFF
val b2 = src(offset + 1) & 0xFF
offset += 2
cp(dst) = b & 0x3F | b1 << 6 | b2 << 14
}
dst += 1
}
(buf: java.nio.Buffer).position(offset)
// for really long strings, convert in pieces to avoid JS engine overflows
if (len > 4096) {
offset = 0
var s = ""
while (offset < len) {
s += charArray2String(cp, offset, math.min(4096, len - offset))
offset += 4096
}
s
} else {
charArray2String(cp, 0, len)
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/.jvm/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/DefaultByteBufferProvider.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
private[reflect] object DefaultByteBufferProvider extends DefaultByteBufferProviderFuncs {
override def provider = new HeapByteBufferProvider
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/.jvm/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/ReferenceEquality.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
private[boopickle] object ReferenceEquality {
@inline def eq(a: AnyRef, b: AnyRef): Boolean = a eq b
@inline def ne(a: AnyRef, b: AnyRef): Boolean = a ne b
@inline def identityHashCode(obj: AnyRef): Int = System.identityHashCode(obj)
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/.jvm/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/StringCodec.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
private[reflect] object StringCodec extends StringCodecBase {
override def decodeUTF8(len: Int, buf: ByteBuffer): String = {
val a = new Array[Byte](len)
buf.get(a)
new String(a, StandardCharsets.UTF_8)
}
override def encodeUTF8(str: String): ByteBuffer = {
ByteBuffer.wrap(str.getBytes(StandardCharsets.UTF_8))
}
override def decodeUTF16(len: Int, buf: ByteBuffer): String = {
val a = new Array[Byte](len)
buf.get(a)
new String(a, StandardCharsets.UTF_16LE)
}
override def encodeUTF16(str: String): ByteBuffer = {
ByteBuffer.wrap(str.getBytes(StandardCharsets.UTF_16LE))
}
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/.native/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/DefaultByteBufferProvider.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
object DefaultByteBufferProvider extends DefaultByteBufferProviderFuncs {
override def provider = new HeapByteBufferProvider
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/.native/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/ReferenceEquality.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
object ReferenceEquality {
@inline def eq(a: AnyRef, b: AnyRef): Boolean = a == b
@inline def ne(a: AnyRef, b: AnyRef): Boolean = a != b
@inline def identityHashCode(obj: AnyRef): Int = obj.hashCode()
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/.native/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/StringCodec.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
object StringCodec extends StringCodecBase {
override def decodeUTF8(len: Int, buf: ByteBuffer): String = {
val a = new Array[Byte](len)
buf.get(a)
new String(a, StandardCharsets.UTF_8)
}
override def encodeUTF8(str: String): ByteBuffer = {
ByteBuffer.wrap(str.getBytes(StandardCharsets.UTF_8))
}
override def decodeUTF16(len: Int, buf: ByteBuffer): String = {
val a = new Array[Byte](len)
buf.get(a)
new String(a, StandardCharsets.UTF_16LE)
}
override def encodeUTF16(str: String): ByteBuffer = {
ByteBuffer.wrap(str.getBytes(StandardCharsets.UTF_16LE))
}
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/BufferPool.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
import java.nio.ByteBuffer
import java.util.concurrent.atomic.AtomicInteger
private[reflect] object BufferPool {
// two pools for two different size categories
private final val poolEntrySize0 = ByteBufferProvider.initSize
private final val poolEntrySize1 = ByteBufferProvider.expandSize + 16
// maximum size of a ByteBuffer to be included in a pool
private final val maxBufferSize = poolEntrySize1 * 2
private final val entryCount = 1024
private var disablePool = false
final class Pool {
private val pool0 = new Array[ByteBuffer](entryCount)
private val pool1 = new Array[ByteBuffer](entryCount)
private val allocIdx0 = new AtomicInteger(0)
private val allocIdx1 = new AtomicInteger(0)
private val releaseIdx0 = new AtomicInteger(0)
private val releaseIdx1 = new AtomicInteger(0)
// for collecting some performance characteristics
var allocOk = 0
var allocMiss = 0
def allocate(minSize: Int): Option[ByteBuffer] = {
if (disablePool) {
None
} else if (minSize > poolEntrySize1) {
allocMiss += 1
None
} else if (minSize > poolEntrySize0 || allocIdx0.get() == releaseIdx0.get()) {
// allocate from pool1
val aIdx = allocIdx1.get()
val rIdx = releaseIdx1.get()
val aNext = (aIdx + 1) % entryCount
if (aIdx != rIdx) {
// try to allocate
val result = Some(pool1(aNext))
if (allocIdx1.compareAndSet(aIdx, aNext)) {
allocOk += 1
result
} else {
allocMiss += 1
None
}
} else {
allocMiss += 1
None
}
} else {
// allocate from pool0
val aIdx = allocIdx0.get()
val rIdx = releaseIdx0.get()
val aNext = (aIdx + 1) % entryCount
if (aIdx != rIdx) {
// try to allocate
val result = Some(pool0(aNext))
if (allocIdx0.compareAndSet(aIdx, aNext)) {
allocOk += 1
result
} else {
allocMiss += 1
None
}
} else {
allocMiss += 1
None
}
}
}
def release(bb: ByteBuffer): Unit = {
if (!disablePool) {
// do not take large buffers into the pool, as their reallocation is relatively cheap
val bufSize = bb.capacity
if (bufSize < maxBufferSize && bufSize >= poolEntrySize0) {
if (bufSize >= poolEntrySize1) {
val aIdx = allocIdx1.get()
val rIdx = releaseIdx1.get()
val rNext = (rIdx + 1) % entryCount
if (rNext != aIdx) {
// try to release the buffer
(bb: java.nio.Buffer).clear()
pool1(rNext) = bb
releaseIdx1.compareAndSet(rIdx, rNext)
()
}
} else {
val aIdx = allocIdx0.get()
val rIdx = releaseIdx0.get()
val rNext = (rIdx + 1) % entryCount
if (rNext != aIdx) {
// try to release the buffer
(bb: java.nio.Buffer).clear()
pool0(rNext) = bb
releaseIdx0.compareAndSet(rIdx, rNext)
()
}
}
}
}
}
}
val heapPool = new Pool
val directPool = new Pool
def allocate(minSize: Int): Option[ByteBuffer] = {
heapPool.allocate(minSize)
}
def allocateDirect(minSize: Int): Option[ByteBuffer] = {
directPool.allocate(minSize)
}
def release(bb: ByteBuffer): Unit = {
if (bb.isDirect)
directPool.release(bb)
else
heapPool.release(bb)
}
def isDisabled = disablePool
def disable(): Unit = disablePool = true
def enable(): Unit = disablePool = false
def allocOk = heapPool.allocOk + directPool.allocOk
def allocMiss = heapPool.allocMiss + directPool.allocMiss
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/BufferProvider.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
import java.nio.{ByteBuffer, ByteOrder}
private[reflect] trait BufferProvider {
/**
* Makes sure the ByteBuffer has enough space for new data. If not, allocates a new ByteBuffer
* and returns it. The returned ByteBuffer must have little-endian ordering.
*
* @param size Number of bytes needed for new data
* @return
*/
def alloc(size: Int): ByteBuffer
/**
* Completes the encoding and returns the ByteBuffer, merging the chain of buffers if necessary
*
* @return
*/
def asByteBuffer: ByteBuffer
}
private[reflect] abstract class ByteBufferProvider extends BufferProvider {
import ByteBufferProvider._
protected val pool = BufferPool
protected var buffers: List[ByteBuffer] = Nil
protected var currentBuf: ByteBuffer = allocate(initSize)
protected def allocate(size: Int): ByteBuffer
final private def newBuffer(size: Int): Unit = {
// flip current buffer (prepare for reading and set limit)
(currentBuf: java.nio.Buffer).flip()
buffers = currentBuf :: buffers
// replace current buffer with the new one, align to 16-byte border for small sizes
currentBuf = allocate((math.max(size, expandSize) & ~15) + 16)
}
@inline final def alloc(size: Int): ByteBuffer = {
if (currentBuf.remaining() < size)
newBuffer(size)
currentBuf
}
def asByteBuffer = {
(currentBuf: java.nio.Buffer).flip()
if (buffers.isEmpty) currentBuf
else {
val bufList = (currentBuf :: buffers).reverse
// create a new buffer and combine all buffers into it
val comb = allocate(bufList.map(_.limit()).sum)
bufList.foreach(buf => comb.put(buf))
(comb: java.nio.Buffer).flip()
comb
}
}
}
private[reflect] object ByteBufferProvider {
final val initSize = 512
final val expandSize = initSize * 8
}
private[reflect] class HeapByteBufferProvider extends ByteBufferProvider {
override protected def allocate(size: Int) = {
if (pool.isDisabled)
ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN)
else
pool.allocate(size).getOrElse(ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN))
}
override def asByteBuffer = {
(currentBuf: java.nio.Buffer).flip()
if (buffers.isEmpty) currentBuf
else {
// create a new buffer and combine all buffers into it
val bufList = (currentBuf :: buffers).reverse
val comb = allocate(bufList.map(_.limit()).sum)
bufList.foreach {
buf =>
// use fast array copy
java.lang.System.arraycopy(buf.array(), buf.arrayOffset(), comb.array(), comb.position(), buf.limit())
(comb: java.nio.Buffer).position(comb.position() + buf.limit())
// release to the pool
pool.release(buf)
}
(comb: java.nio.Buffer).flip()
comb
}
}
}
private[reflect] trait DefaultByteBufferProviderFuncs {
def provider: ByteBufferProvider
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/CodecSize.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
import java.nio.ByteBuffer
private[reflect] class DecoderSize(val buf: ByteBuffer) extends Decoder {
val stringCodec: StringCodecBase = StringCodec
/** Decodes a single byte
*
* @return
*/
def readByte: Byte = {
buf.get
}
/** Decodes a 32-bit integer (1-5 bytes)
*
* 0XXX XXXX = 0 to 127
* 1000 XXXX b0 = 128 to 4095
* 1001 XXXX b0 = -1 to -4095
* 1010 XXXX b0 b1 = 4096 to 1048575
* 1011 XXXX b0 b1 = -4096 to -1048575
* 1100 XXXX b0 b1 b2 = 1048576 to 268435455
* 1101 XXXX b0 b1 b2 = -1048576 to -268435455
* 1110 0000 b0 b1 b2 b3 = MinInt to MaxInt
* 1111 ???? = reserved for special codings
*
*
* @return
*/
def readInt: Int = {
val b = buf.get & 0xFF
if ((b & 0x80) != 0) {
// special coding, expand sign bit
val sign = if ((b & 0x10) == 0) 1 else -1
val b0 = b & 0xF
b >> 4 match {
case 0x8 | 0x9 =>
val b1 = buf.get & 0xFF
sign * (b0 << 8 | b1)
case 0xA | 0xB =>
val b1 = buf.get & 0xFF
val b2 = buf.get & 0xFF
sign * (b0 << 16 | b1 << 8 | b2)
case 0xC | 0xD =>
val b1 = buf.get & 0xFF
val b2 = buf.get & 0xFF
val b3 = buf.get & 0xFF
sign * (b0 << 24 | b1 << 16 | b2 << 8 | b3)
case 0xE if b == 0xE0 =>
sign * readRawInt
case _ =>
throw new IllegalArgumentException("Unknown integer coding")
}
} else {
b
}
}
def readRawInt: Int = {
buf.getInt
}
/** Decodes a UTF-8 encoded string
*
* @return
*/
def readString: String = {
// read string length
val len = readInt
stringCodec.decodeFast(len, buf)
}
/** Decodes a UTF-8 encoded string whose length is already known
*
* @param len Length of the string (in bytes)
* @return
*/
def readString(len: Int): String = {
stringCodec.decodeFast(len, buf)
}
}
private[reflect] class EncoderSize(bufferProvider: BufferProvider = DefaultByteBufferProvider.provider) extends Encoder {
val stringCodec: StringCodecBase = StringCodec
@inline private def alloc(size: Int): ByteBuffer = bufferProvider.alloc(size)
/** Encodes a single byte
*
* @param b Byte to encode
* @return
*/
def writeByte(b: Byte): Encoder = {
alloc(1).put(b)
this
}
/** Encodes an integer efficiently in 1 to 5 bytes
*
* 0XXX XXXX = 0 to 127
* 1000 XXXX b0 = 128 to 4095
* 1001 XXXX b0 = -1 to -4095
* 1010 XXXX b0 b1 = 4096 to 1048575
* 1011 XXXX b0 b1 = -4096 to -1048575
* 1100 XXXX b0 b1 b2 = 1048575 to 268435455
* 1101 XXXX b0 b1 b2 = -1048575 to -268435455
* 1110 0000 b0 b1 b2 b3 = MinInt to MaxInt
* 1111 ???? = reserved for special codings
*
*
* @param i Integer to encode
*/
def writeInt(i: Int): Encoder = {
// check for a short number
if (i >= 0 && i < 128) {
alloc(1).put(i.toByte)
} else {
if (i > -268435456 && i < 268435456) {
val mask = i >>> 31 << 4
val a = Math.abs(i)
if (a < 4096) {
alloc(2).put((mask | 0x80 | (a >> 8)).toByte).put((a & 0xFF).toByte)
} else if (a < 1048576) {
alloc(3).put((mask | 0xA0 | (a >> 16)).toByte).put(((a >> 8) & 0xFF).toByte).put((a & 0xFF).toByte)
} else {
alloc(4)
.put((mask | 0xC0 | (a >> 24)).toByte)
.put(((a >> 16) & 0xFF).toByte)
.put(((a >> 8) & 0xFF).toByte)
.put((a & 0xFF).toByte)
}
} else {
alloc(5).put(0xE0.toByte).putInt(i)
}
}
this
}
/** Encodes a string using UTF8
*
* @param s String to encode
* @return
*/
def writeString(s: String): Encoder = {
writeInt(s.length)
val bb = alloc(s.length * 3)
stringCodec.encodeFast(s, bb)
this
}
/** Completes the encoding and returns the ByteBuffer
*
* @return
*/
def asByteBuffer: ByteBuffer = bufferProvider.asByteBuffer
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/Codecs.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
import java.nio.ByteBuffer
private[reflect] trait Decoder {
/** Decodes a single byte
*/
def readByte: Byte
/** Decodes a 32-bit integer
*/
def readInt: Int
/** Decodes a string
*/
def readString: String
/** Decodes a string whose length is already known
*
* @param len Length of the string (in bytes)
*/
def readString(len: Int): String
}
private[reflect] trait Encoder {
/** Encodes a single byte
*
* @param b Byte to encode
*/
def writeByte(b: Byte): Encoder
/** Encodes an integer
*/
def writeInt(i: Int): Encoder
/** Encodes a string
*
* @param s String to encode
*/
def writeString(s: String): Encoder
/** Completes the encoding and returns the ByteBuffer
*/
def asByteBuffer: ByteBuffer
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/CompositePicklers.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
import scala.collection.mutable
import scala.reflect.ClassTag
/** Encodes a class belonging to a type hierarchy. Type is identified by the index in the `picklers` sequence, so care
* must be taken to ensure picklers are added in the same order.
*/
private[reflect] class CompositePickler[A] extends Pickler[A] {
import Constants._
private var picklerClasses = IdentMap.empty
private val picklers = mutable.ArrayBuffer.empty[(Class[_], Pickler[_])]
override def pickle(obj: A)(implicit state: PickleState): Unit = {
if (obj == null) {
state.enc.writeInt(NullObject)
} else {
val clz = obj.getClass
picklerClasses(clz) match {
case None =>
throw new IllegalArgumentException(s"This CompositePickler doesn't know class '${clz.getName}'.")
case Some(idx) =>
val pickler = picklers(idx - 2)._2
state.enc.writeInt(idx - 1)
pickler.asInstanceOf[Pickler[A]].pickle(obj)
}
}
}
override def unpickle(implicit state: UnpickleState): A = {
val idx = state.dec.readInt
if (idx == NullObject) null.asInstanceOf[A]
else {
if (idx < 0 || idx > picklers.size)
throw new IllegalStateException(s"Index $idx is not defined in this CompositePickler")
picklers(idx - 1)._2.asInstanceOf[Pickler[A]].unpickle
}
}
private def addPickler[B](pickler: Pickler[B], tag: ClassTag[B]): Unit = {
val clz = tag.runtimeClass
if (picklerClasses(clz).isDefined)
throw new IllegalArgumentException(s"Cannot add same class (${clz.getName}) twice to a composite pickler")
picklerClasses = picklerClasses.updated(clz)
picklers.append((clz, pickler))
}
@noinline def addConcreteType[B <: A](implicit pickler: Pickler[B], tag: ClassTag[B]): CompositePickler[A] = {
addPickler(pickler, tag)
this
}
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/Constants.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
private[boopickle] object Constants {
final val NullRef = -1
final val NullObject = 0
// codes for Option
final val OptionNone: Int = 1
final val OptionSome: Int = 2
// common strings that can be used as references
val immutableInitData = Seq("null", "true", "false", "0", "1", "-1", "2", "3", "4", "5", "6", "7", "8", "9")
val identityInitData = Seq(None)
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/Default.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
private[reflect] trait BasicImplicitPicklers extends PicklerHelper with XCompatImplicitPicklers {
implicit def booleanPickler: P[Boolean] = BasicPicklers.BooleanPickler
implicit def intPickler: P[Int] = BasicPicklers.IntPickler
implicit def stringPickler: P[String] = BasicPicklers.StringPickler
implicit def optionPickler[T: P]: P[Option[T]] = BasicPicklers.OptionPickler[T]
}
private[reflect] object PickleImpl {
@inline def apply[A](value: A)(implicit state: PickleState, p: Pickler[A]): PickleState = {
p.pickle(value)(state)
state
}
@inline def intoBytes[A](value: A)(implicit state: PickleState, p: Pickler[A]): ByteBuffer = {
apply(value).toByteBuffer
}
@inline private[reflect] def serializeIntoString[A](a: A, pickler: Pickler[A]): String = {
val pickleState = PickleState.pickleStateSpeed
val buf = PickleImpl.intoBytes(a)(pickleState, pickler)
new String(buf.array(), buf.arrayOffset(), buf.limit(), StandardCharsets.ISO_8859_1)
}
}
private[reflect] trait Base {
type Pickler[A] = _root_.izumi.reflect.thirdparty.internal.boopickle.Pickler[A]
type PickleState = _root_.izumi.reflect.thirdparty.internal.boopickle.PickleState
}
private[reflect] object NoMacro extends Base with BasicImplicitPicklers with TuplePicklers
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/IdentList.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
import scala.collection.mutable
/**
* Specialized fast and cheap to initialize identity list for unpickle state identifier refs
*/
private[reflect] abstract class IdentList {
@noinline def apply(idx: Int): AnyRef
@noinline def updated(obj: AnyRef): IdentList
}
private[reflect] object IdentList {
private[boopickle] final class Entry(val obj: AnyRef, var next: Entry)
private[boopickle] val maxSize = 32
}
private[reflect] object EmptyIdentList extends IdentList {
override def apply(idx: Int): AnyRef = throw new IndexOutOfBoundsException
override def updated(obj: AnyRef): IdentList = new IdentList1Plus(obj)
}
/**
* A fast and simple linked list implementation for identifier list
*
* @param o1 First object
*/
private[boopickle] final class IdentList1Plus(o1: AnyRef) extends IdentList {
import IdentList.Entry
var last: Entry = new Entry(o1, null)
var head: Entry = last
var switchOver = false
var size = 0
override def apply(idx: Int): AnyRef = {
// first time something is looked up, we switch to the more efficient implementation
switchOver = true
var i = 0
var e = head
while (i < idx && e != null) {
i += 1
e = e.next
}
if (e == null)
throw new IndexOutOfBoundsException
e.obj
}
override def updated(obj: AnyRef): IdentList = {
val e = new Entry(obj, null)
last.next = e
last = e
size += 1
if (switchOver || size > IdentList.maxSize)
new IdentListBig(head, size)
else
this
}
}
/**
* A more scalable implementation for an identifier list
*
* @param first First entry in a list of entries
*/
private[boopickle] final class IdentListBig(first: IdentList.Entry, size: Int) extends IdentList {
// transform the linked list into an array buffer
val b = mutable.ArrayBuffer.newBuilder[AnyRef]
b.sizeHint(size)
var e = first
while (e != null) {
b += e.obj
e = e.next
}
val entries = b.result()
override def apply(idx: Int): AnyRef = {
entries(idx)
}
override def updated(obj: AnyRef): IdentList = {
entries += obj
this
}
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/IdentMap.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
/**
* Specialized fast and cheap to initialize identity map for pickle state identifier map
*/
private[reflect] abstract class IdentMap {
def apply(obj: AnyRef): Option[Int]
def updated(obj: AnyRef): IdentMap
}
private[reflect] object IdentMap {
def empty: IdentMap = EmptyIdentMap
}
private[reflect] object EmptyIdentMap extends IdentMap {
override def apply(obj: AnyRef): Option[Int] = None
override def updated(obj: AnyRef): IdentMap = new IdentMap1(obj)
}
private[boopickle] final class IdentMap1(o1: AnyRef) extends IdentMap {
override def apply(obj: AnyRef): Option[Int] = {
if (ReferenceEquality.eq(obj, o1))
Some(2)
else None
}
override def updated(obj: AnyRef): IdentMap = new IdentMap2(o1, obj)
}
private[boopickle] final class IdentMap2(o1: AnyRef, o2: AnyRef) extends IdentMap {
override def apply(obj: AnyRef): Option[Int] = {
if (ReferenceEquality.eq(obj, o1))
Some(2)
else if (ReferenceEquality.eq(obj, o2))
Some(3)
else None
}
override def updated(obj: AnyRef): IdentMap = new IdentMap3Plus(o1, o2, obj)
}
private[reflect] object IdentMap3Plus {
private[boopickle] class Entry(val hash: Int, val obj: AnyRef, val idx: Int, var next: Entry)
}
private[boopickle] final class IdentMap3Plus(o1: AnyRef, o2: AnyRef, o3: AnyRef) extends IdentMap {
import IdentMap3Plus.Entry
var hashSize = 64
val maxDepth = 1
var hashTable = new Array[Entry](hashSize)
// indices 0 (not used) and 1 (for null) are reserved
var curIdx = 2
// initialize with data
updated(o1)
updated(o2)
updated(o3)
@inline private def hashIdx(hash: Int) = {
val h = scala.util.hashing.byteswap32(hash)
((h >> 16) ^ (h >> 8) ^ h) & (hashSize - 1)
}
override def apply(obj: AnyRef): Option[Int] = {
val hash = ReferenceEquality.identityHashCode(obj)
val tableIdx = hashIdx(hash)
var e = hashTable(tableIdx)
while ((e != null) && ReferenceEquality.ne(e.obj, obj)) e = e.next
if (e == null)
None
else
Some(e.idx)
}
override def updated(obj: AnyRef): IdentMap = {
val hash = ReferenceEquality.identityHashCode(obj)
val tableIdx = hashIdx(hash)
hashTable(tableIdx) = new Entry(hash, obj, curIdx, hashTable(tableIdx))
curIdx += 1
// should we switch to next gear?
if (curIdx > hashSize * maxDepth)
resize()
this
}
/**
* Resizes the underlying hash table to make indexing fast as the number of entries grows
*/
private def resize(): Unit = {
val newSize = hashSize * 4
val newTable = new Array[Entry](newSize)
// copy old entries
var i = hashSize - 1
hashSize = newSize
while (i >= 0) {
var e = hashTable(i)
while (e != null) {
val tableIdx = hashIdx(e.hash)
val n = e.next
e.next = newTable(tableIdx)
newTable(tableIdx) = e
e = n
}
i -= 1
}
hashTable = newTable
}
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/Pickler.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
import java.nio.ByteBuffer
import scala.collection.mutable
private[reflect] trait Pickler[A] {
def pickle(obj: A)(implicit state: PickleState): Unit
def unpickle(implicit state: UnpickleState): A
def xmap[B](ab: A => B)(ba: B => A): Pickler[B] = {
val self = this
new Pickler[B] {
override def unpickle(implicit state: UnpickleState): B = {
ab(self.unpickle(state))
}
override def pickle(obj: B)(implicit state: PickleState): Unit = {
self.pickle(ba(obj))
}
}
}
}
private[reflect] trait PicklerHelper {
protected type P[A] = Pickler[A]
/** Helper function to write pickled types
*/
protected def write[A](value: A)(implicit state: PickleState, p: P[A]): Unit = p.pickle(value)(state)
/** Helper function to unpickle a type
*/
protected def read[A](implicit state: UnpickleState, u: P[A]): A = u.unpickle
}
private[reflect] object BasicPicklers extends PicklerHelper with XCompatPicklers {
import Constants._
object BooleanPickler extends P[Boolean] {
@inline override def pickle(value: Boolean)(implicit state: PickleState): Unit = state.enc.writeByte(if (value) 1 else 0)
@inline override def unpickle(implicit state: UnpickleState): Boolean = {
state.dec.readByte match {
case 1 => true
case 0 => false
case x => throw new IllegalArgumentException(s"Invalid value $x for Boolean")
}
}
}
object IntPickler extends P[Int] {
@inline override def pickle(value: Int)(implicit state: PickleState): Unit = state.enc.writeInt(value)
@inline override def unpickle(implicit state: UnpickleState): Int = state.dec.readInt
}
object StringPickler extends P[String] {
override def pickle(s: String)(implicit state: PickleState): Unit = {
state.immutableRefFor(s) match {
case Some(idx) =>
state.enc.writeInt(-idx)
case None =>
if (s.isEmpty) {
state.enc.writeInt(0)
} else {
state.enc.writeString(s)
state.addImmutableRef(s)
}
}
}
override def unpickle(implicit state: UnpickleState): String = {
val len = state.dec.readInt
if (len < 0) {
state.immutableFor[String](-len)
} else if (len == 0) {
""
} else {
val s = state.dec.readString(len)
state.addImmutableRef(s)
s
}
}
}
def OptionPickler[T: P]: P[Option[T]] = new P[Option[T]] {
override def pickle(obj: Option[T])(implicit state: PickleState): Unit = {
obj match {
case null =>
state.enc.writeInt(NullObject)
case Some(x) =>
state.enc.writeInt(OptionSome.toInt)
write[T](x)
case None =>
// `None` is always encoded as zero
state.enc.writeInt(OptionNone.toInt)
}
}
override def unpickle(implicit state: UnpickleState): Option[T] = {
state.dec.readInt match {
case NullObject =>
null
case OptionSome =>
val o = Some(read[T])
o
case OptionNone =>
None
case _ =>
throw new IllegalArgumentException("Invalid coding for Option type")
}
}
}
}
/** Manage state for a pickling "session".
*
* @param enc Encoder instance to use
* @param dedupImmutable Set to `false` if you want to disable deduplication of immutable values (like Strings)
*/
private[reflect] final class PickleState(val enc: Encoder, dedupImmutable: Boolean = true) {
@inline def identityRefFor(obj: AnyRef): Option[Int] = None
@inline def addIdentityRef(obj: AnyRef): Unit = ()
/** Object reference for immutable pickled objects
*/
private[this] var immutableRefs: mutable.AnyRefMap[AnyRef, Int] = null
private[this] var immutableIdx = 2
@inline def immutableRefFor(obj: AnyRef): Option[Int] = {
if (obj == null)
Some(1)
else if (!dedupImmutable)
None
else if (immutableRefs != null)
immutableRefs.get(obj)
else
None
}
@inline def addImmutableRef(obj: AnyRef): Unit = {
if (dedupImmutable) {
if (immutableRefs == null)
immutableRefs = mutable.AnyRefMap.empty
immutableRefs.update(obj, immutableIdx)
immutableIdx += 1
}
}
@inline def pickle[A](value: A)(implicit p: Pickler[A]): PickleState = {
p.pickle(value)(this)
this
}
def toByteBuffer: ByteBuffer = enc.asByteBuffer
}
private[reflect] object PickleState {
/** Provides a default PickleState if none is available implicitly
*
* @return
*/
implicit def pickleStateSpeed: PickleState = new PickleState(new EncoderSize, dedupImmutable = true)
}
/** Manage state for an unpickling "session"
*
* @param dec Decoder instance to use
* @param deduplicate Set to `false` if you want to disable deduplication
* @param dedupImmutable Set to `false` if you want to disable deduplication of immutable values (like Strings)
*/
private[reflect] final class UnpickleState(val dec: Decoder, deduplicate: Boolean = true, dedupImmutable: Boolean = true) {
/** Object reference for pickled objects (use identity for equality comparison)
*
* Index 0 is not used
* Index 1 = null
* Index 2-n, references to pickled objects
*/
private[this] var identityRefs: IdentList = EmptyIdentList
@noinline def codingError(code: Int): Nothing = {
throw new IllegalArgumentException(s"Unknown object coding: $code")
}
@noinline def identityFor[A <: AnyRef](ref: Int): A = {
if (ref < 2)
null.asInstanceOf[A]
else if (!deduplicate)
throw new Exception("Deduplication is disabled, but identityFor was called.")
else
identityRefs(ref - 2).asInstanceOf[A]
}
@inline def addIdentityRef(obj: AnyRef): Unit =
if (deduplicate)
identityRefs = identityRefs.updated(obj)
/** Object reference for immutable pickled objects
*/
private[this] var immutableRefs: IdentList = EmptyIdentList
@noinline def immutableFor[A <: AnyRef](ref: Int): A = {
if (ref < 2)
null.asInstanceOf[A]
else if (dedupImmutable)
immutableRefs(ref - 2).asInstanceOf[A]
else
throw new Exception("Deduplication for immutable objects is disabled, but immutableFor was called.")
}
@inline def addImmutableRef(obj: AnyRef): Unit = {
if (dedupImmutable)
immutableRefs = immutableRefs.updated(obj)
}
@inline def unpickle[A](implicit u: Pickler[A]): A = u.unpickle(this)
}
private[reflect] object UnpickleState {
def apply(bytes: ByteBuffer) = new UnpickleState(new DecoderSize(bytes))
def apply(decoder: Decoder, deduplicate: Boolean = false, dedupImmutable: Boolean = false) =
new UnpickleState(decoder, deduplicate)
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/StringCodecBase.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
import java.nio.ByteBuffer
private[reflect] abstract class StringCodecFast {
/**
* String decoding function for a special 1-3 byte encoding of 16-bit char values
*
* @param len How many bytes to decode
* @param buf Buffer containing the data
* @return String with decoded data
*/
def decodeFast(len: Int, buf: ByteBuffer): String = {
if (buf.hasArray)
decodeFastArray(len, buf)
else
decodeFastBuf(len, buf)
}
/**
* String encoding function for a special 1-3 byte encoding of 16-bit char values
*
* @param s String to encode
* @return `ByteBuffer` with the encoded data
*/
def encodeFast(s: String, bb: ByteBuffer): Unit = {
if (bb.hasArray)
encodeFastArray(s, bb)
else
encodeFastBuf(s, bb)
}
def encodeFastArray(s: String, bb: ByteBuffer): Unit = {
val len = s.length()
val buf = bb.array()
var dst = bb.arrayOffset() + bb.position()
var src = 0
var c: Char = ' '
// start by encoding ASCII only
while ((src < len) && { c = s.charAt(src); c < 0x80 }) {
buf(dst) = c.toByte
src += 1
dst += 1
}
// next stage, encode also non-ASCII
while (src < len) {
c = s.charAt(src)
if (c < 0x80) {
buf(dst) = c.toByte
dst += 1
} else if (c < 0x4000) {
buf(dst) = (0x80 | (c & 0x3F)).toByte
buf(dst + 1) = (c >> 6 & 0xFF).toByte
dst += 2
} else {
buf(dst) = (0xC0 | (c & 0x3F)).toByte
buf(dst + 1) = (c >> 6 & 0xFF).toByte
buf(dst + 2) = (c >> 14).toByte
dst += 3
}
src += 1
}
(bb: java.nio.Buffer).position(dst - bb.arrayOffset())
}
def encodeFastBuf(s: String, bb: ByteBuffer): Unit = {
val len = s.length()
// worst case scenario produces 3 bytes per character
var src = 0
var c: Char = ' '
// start by encoding ASCII only
while ((src < len) && { c = s.charAt(src); c < 0x80 }) {
bb.put(c.toByte)
src += 1
}
// next stage, encode also non-ASCII
while (src < len) {
c = s.charAt(src)
if (c < 0x80) {
bb.put(c.toByte)
} else if (c < 0x4000) {
bb.put((0x80 | (c & 0x3F)).toByte)
bb.put((c >> 6 & 0xFF).toByte)
} else {
bb.put((0xC0 | (c & 0x3F)).toByte)
bb.put((c >> 6 & 0xFF).toByte)
bb.put((c >> 14).toByte)
}
src += 1
}
}
/**
* Faster decoding for array backed buffers
*/
protected def decodeFastArray(len: Int, buf: ByteBuffer): String = {
val cp = new Array[Char](len)
val src = buf.array()
var offset = buf.arrayOffset() + buf.position()
var dst = 0
while (dst < len) {
val b = src(offset)
offset += 1
if ((b & 0x80) == 0) {
cp(dst) = (b & 0x7F).toChar
} else if ((b & 0xC0) == 0x80) {
val b1 = src(offset)
offset += 1
cp(dst) = (b & 0x3F | (b1.toShort & 0xFF) << 6).toChar
} else {
val b1 = src(offset)
val b2 = src(offset + 1)
offset += 2
cp(dst) = (b & 0x3F | (b1.toShort & 0xFF) << 6 | (b2.toShort << 14)).toChar
}
dst += 1
}
(buf: java.nio.Buffer).position(offset - buf.arrayOffset())
new String(cp)
}
/**
* Decoding for normal non-array `ByteBuffer`
*/
protected def decodeFastBuf(len: Int, buf: ByteBuffer): String = {
val cp = new Array[Char](len)
var i = 0
var dst = 0
while (dst < len) {
val b = buf.get()
if ((b & 0x80) == 0) {
cp(dst) = (b & 0x7F).toChar
} else if ((b & 0xC0) == 0x80) {
val b1 = buf.get()
i += 1
cp(dst) = (b & 0x3F | (b1.toShort & 0xFF) << 6).toChar
} else {
val b1 = buf.get()
val b2 = buf.get()
i += 2
cp(dst) = (b & 0x3F | (b1.toShort & 0xFF) << 6 | (b2.toShort << 14)).toChar
}
i += 1
dst += 1
}
new String(cp)
}
}
private[reflect] abstract class StringCodecBase extends StringCodecFast {
def decodeUTF8(len: Int, buf: ByteBuffer): String
def encodeUTF8(s: String): ByteBuffer
def decodeUTF16(len: Int, buf: ByteBuffer): String
def encodeUTF16(s: String): ByteBuffer
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/src/main/scala/izumi/reflect/thirdparty/internal/boopickle/TuplePicklers.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
private[reflect] trait TuplePicklers extends PicklerHelper {
implicit def Tuple2Pickler[T1: P, T2: P]: P[(T1, T2)] = new P[(T1, T2)] {
override def pickle(x: (T1, T2))(implicit state: PickleState): Unit = { write[T1](x._1); write[T2](x._2) }
override def unpickle(implicit state: UnpickleState): (T1, T2) = (read[T1], read[T2])
}
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/src/main/scala-2.12-/izumi/reflect/thirdparty/internal/boopickle/XCompat.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package izumi.reflect.thirdparty.internal.boopickle
import izumi.reflect.thirdparty.internal.boopickle.Constants.NullRef
import scala.collection.generic.CanBuildFrom
private[reflect] trait XCompatImplicitPicklers {
this: PicklerHelper =>
implicit def mapPickler[T: P, S: P, V[_, _] <: scala.collection.Map[_, _]](
implicit cbf: CanBuildFrom[Nothing, (T, S), V[T, S]]
): P[V[T, S]] =
BasicPicklers.MapPickler[T, S, V]
implicit def iterablePickler[T: P, V[_] <: Iterable[_]](implicit cbf: CanBuildFrom[Nothing, T, V[T]]): P[V[T]] =
BasicPicklers.IterablePickler[T, V]
}
private[reflect] trait XCompatPicklers {
this: PicklerHelper =>
/**
* This pickler works on all collections that derive from Iterable[T] (Vector, Set, List, etc)
*
* @tparam T type of the values
* @tparam V type of the collection
* @return
*/
def IterablePickler[T: P, V[_] <: Iterable[_]](implicit cbf: CanBuildFrom[Nothing, T, V[T]]): P[V[T]] = new P[V[T]] {
override def pickle(iterable: V[T])(implicit state: PickleState): Unit = {
if (iterable == null) {
state.enc.writeInt(NullRef)
} else {
// encode length
state.enc.writeInt(iterable.size)
// encode contents
iterable.iterator.asInstanceOf[Iterator[T]].foreach(a => write[T](a))
}
}
override def unpickle(implicit state: UnpickleState): V[T] = {
state.dec.readInt match {
case NullRef =>
null.asInstanceOf[V[T]]
case 0 =>
// empty sequence
val res = cbf().result()
res
case len =>
val b = cbf()
b.sizeHint(len)
var i = 0
while (i < len) {
b += read[T]
i += 1
}
val res = b.result()
res
}
}
}
/**
* Maps require a specific pickler as they have two type parameters.
*
* @tparam T Type of keys
* @tparam S Type of values
* @return
*/
def MapPickler[T: P, S: P, V[_, _] <: scala.collection.Map[_, _]](
implicit cbf: CanBuildFrom[Nothing, (T, S), V[T, S]]
): P[V[T, S]] =
new P[V[T, S]] {
override def pickle(map: V[T, S])(implicit state: PickleState): Unit = {
if (map == null) {
state.enc.writeInt(NullRef)
} else {
// encode length
state.enc.writeInt(map.size)
// encode contents as a sequence
val kPickler = implicitly[P[T]]
val vPickler = implicitly[P[S]]
map.asInstanceOf[scala.collection.Map[T, S]].foreach {
kv =>
kPickler.pickle(kv._1)(state)
vPickler.pickle(kv._2)(state)
}
}
}
override def unpickle(implicit state: UnpickleState): V[T, S] = {
state.dec.readInt match {
case NullRef =>
null.asInstanceOf[V[T, S]]
case 0 =>
// empty map
val res = cbf().result()
res
case idx if idx < 0 =>
state.identityFor[V[T, S]](-idx)
case len =>
val b = cbf()
b.sizeHint(len)
val kPickler = implicitly[P[T]]
val vPickler = implicitly[P[S]]
var i = 0
while (i < len) {
b += kPickler.unpickle(state) -> vPickler.unpickle(state)
i += 1
}
val res = b.result()
res
}
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/src/main/scala-2.13+/izumi/reflect/thirdparty/internal/boopickle/XCompat.scala
================================================
package izumi.reflect.thirdparty.internal.boopickle
import Constants.NullRef
import scala.collection.Factory
private[reflect] trait XCompatImplicitPicklers {
this: PicklerHelper =>
implicit def mapPickler[T: P, S: P, V[_, _] <: scala.collection.Map[_, _]](
implicit cbf: Factory[(T, S), V[T, S]]
): P[V[T, S]] = BasicPicklers.MapPickler[T, S, V]
implicit def iterablePickler[T: P, V[_] <: Iterable[_]](implicit cbf: Factory[T, V[T]]): P[V[T]] =
BasicPicklers.IterablePickler[T, V]
}
private[reflect] trait XCompatPicklers {
this: PicklerHelper =>
/**
* This pickler works on all collections that derive from Iterable[T] (Vector, Set, List, etc)
*
* @tparam T type of the values
* @tparam V type of the collection
* @return
*/
def IterablePickler[T: P, V[_] <: Iterable[_]](implicit cbf: Factory[T, V[T]]): P[V[T]] = new P[V[T]] {
override def pickle(iterable: V[T])(implicit state: PickleState): Unit = {
if (iterable == null) {
state.enc.writeInt(NullRef)
} else {
// encode length
state.enc.writeInt(iterable.size)
// encode contents
iterable.iterator.asInstanceOf[Iterator[T]].foreach(a => write[T](a))
}
}
override def unpickle(implicit state: UnpickleState): V[T] = {
state.dec.readInt match {
case NullRef =>
null.asInstanceOf[V[T]]
case 0 =>
// empty sequence
val res = cbf.newBuilder.result()
res
case len =>
val b = cbf.newBuilder
b.sizeHint(len)
var i = 0
while (i < len) {
b += read[T]
i += 1
}
val res = b.result()
res
}
}
}
/**
* Maps require a specific pickler as they have two type parameters.
*
* @tparam T Type of keys
* @tparam S Type of values
* @return
*/
def MapPickler[T: P, S: P, V[_, _] <: scala.collection.Map[_, _]](implicit cbf: Factory[(T, S), V[T, S]]): P[V[T, S]] =
new P[V[T, S]] {
override def pickle(map: V[T, S])(implicit state: PickleState): Unit = {
if (map == null) {
state.enc.writeInt(NullRef)
} else {
// encode length
state.enc.writeInt(map.size)
// encode contents as a sequence
val kPickler = implicitly[P[T]]
val vPickler = implicitly[P[S]]
map.asInstanceOf[scala.collection.Map[T, S]].foreach {
kv =>
kPickler.pickle(kv._1)(state)
vPickler.pickle(kv._2)(state)
}
}
}
override def unpickle(implicit state: UnpickleState): V[T, S] = {
state.dec.readInt match {
case NullRef =>
null.asInstanceOf[V[T, S]]
case 0 =>
// empty map
val res = cbf.newBuilder.result()
res
case idx if idx < 0 =>
state.identityFor[V[T, S]](-idx)
case len =>
val b = cbf.newBuilder
b.sizeHint(len)
val kPickler = implicitly[P[T]]
val vPickler = implicitly[P[S]]
var i = 0
while (i < len) {
b += kPickler.unpickle(state) -> vPickler.unpickle(state)
i += 1
}
val res = b.result()
res
}
}
}
}
================================================
FILE: izumi-reflect/izumi-reflect-thirdparty-boopickle-shaded/src/test/scala/izumi/reflect/.keep
================================================
================================================
FILE: keys.sh
================================================
#!/bin/bash -e
EMAIL=${EMAIL:-pshirshov@gmail.com}
SSHKEYNAME=travis-deploy-key
PASSPHRASE=$(uuidgen)
OPENSSL_KEY=`openssl rand -hex 32`
OPENSSL_IV=`openssl rand -hex 16`
SECRETS=./.secrets
GPGHOME=$SECRETS/gnupg.home
GPGTARGET=$SECRETS/gnupg
LOCALSBT=$SECRETS/local.sbt
GPGTMP=/tmp/gpginput
PUBRING=$GPGTARGET/pubring.gpg
SECRING=$GPGTARGET/secring.gpg
SSHKEY=$SECRETS/$SSHKEYNAME
echo "GPG Passphrase: $PASSPHRASE"
echo "SECRETS ENCRYPTION:"
echo "OPENSSL_KEY=$OPENSSL_KEY"
echo "OPENSSL_IV=$OPENSSL_IV"
rm -rf $GPGTARGET
rm -rf $GPGHOME
mkdir -p $GPGTARGET
mkdir -p $GPGHOME
chmod 700 $GPGHOME
cat >$GPGTMP < $SECRING
gpg --homedir $GPGHOME --batch --yes --passphrase $PASSPHRASE --pinentry-mode loopback --export > $PUBRING
#sbt shim
rm -f local.sbt
cat >$LOCALSBT < s -> scala300
case s @ "2.12" => s -> scala212
case s @ "2.11" => s -> scala211
case s @ s"2${_}" => s -> scala213
}.map {
case (s, target) =>
targetScala = target :: targetScala.filterNot(_ == target).toList
s
}.orNull
)
if (args.contains("--help")) {
println(
"launch with `./sbtgen.sc 3` to use Scala 3 in IDEA, launch with `./sbtgen.sc 2` to use Scala 2"
)
}
println(s"Scala targets: $targetScala")
Entrypoint.main(izumi_reflect, settings, Seq("-o", ".") ++ newArgs)
}
val settings = GlobalSettings(
groupId = "dev.zio",
sbtVersion = None,
scalaJsVersion = Version.VExpr("PV.scala_js_version"),
scalaNativeVersion = Version.VExpr("PV.scala_native_version"),
crossProjectVersion = Version.VExpr("PV.sbt_crossproject_version")
)
object Deps {
final val scalatest = Library("org.scalatest", "scalatest", V.scalatest, LibraryType.Auto)
final val scala_reflect = Library("org.scala-lang", "scala-reflect", Version.VExpr("scalaVersion.value"), LibraryType.Invariant)
final val scala3_compiler = Library("org.scala-lang", "scala3-compiler", Version.VExpr("scalaVersion.value"), LibraryType.AutoJvm)
final val projector = Library("org.typelevel", "kind-projector", V.kind_projector, LibraryType.Invariant)
.more(LibSetting.Raw("cross CrossVersion.full"))
}
import Deps._
object Groups {
final val izumi_reflect = Set(Group("izumi-reflect"))
}
object Targets {
def targetScala = Izumi.targetScala
private val jvmPlatform = PlatformEnv(
platform = Platform.Jvm,
language = targetScala
)
private val jsPlatform = PlatformEnv(
platform = Platform.Js,
language = targetScala.filterNot(_ == Izumi.scala211),
settings = Seq(
"coverageEnabled" := false,
"scalaJSLinkerConfig" in (SettingScope.Project, Platform.Js) := "scalaJSLinkerConfig.value.withModuleKind(ModuleKind.CommonJSModule)".raw
)
)
private val nativePlatform = PlatformEnv(
platform = Platform.Native,
language = targetScala.filterNot(_ == Izumi.scala211), // scala-native abandoned 2.11
settings = Seq(
"coverageEnabled" := false
)
)
final val crossNative = Seq(jvmPlatform, jsPlatform, nativePlatform)
}
object Projects {
object root {
final val id = ArtifactId("izumi-reflect-root")
final val plugins = Plugins(
enabled = Seq(Plugin("SbtgenVerificationPlugin")),
disabled = Seq.empty
)
final val settings = Seq()
final val sharedAggSettings = Seq(
"crossScalaVersions" := Targets.targetScala.map(_.value),
"scalaVersion" := "crossScalaVersions.value.head".raw
)
final val rootSettings = Defaults.RootOptions ++ Seq(
"crossScalaVersions" := "Nil".raw,
"organization" in SettingScope.Build := "dev.zio",
"publishTo" in SettingScope.Build :=
"""{
| if (isSnapshot.value) {
| Some(
| "central-snapshots" at "https://central.sonatype.com/repository/maven-snapshots/"
| )
| } else {
| localStaging.value
| }
|}""".stripMargin.raw,
"credentials" in SettingScope.Build ++=
"""
|{
|Seq(
| Path.userHome / ".sbt" / "secrets" / "credentials.sonatype-zio-new.properties",
| file(".") / ".secrets" / "credentials.sonatype-nexus.properties"
|)
| .filter(_.exists())
| .map(Credentials.apply)
|}""".stripMargin.raw,
"homepage" in SettingScope.Build := """Some(url("https://zio.dev"))""".raw,
"licenses" in SettingScope.Build := """Seq("Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0"))""".raw,
"developers" in SettingScope.Build :=
"""List(
Developer(id = "jdegoes", name = "John De Goes", url = url("http://degoes.net"), email = "john@degoes.net"),
Developer(id = "7mind", name = "Septimal Mind", url = url("https://github.com/7mind"), email = "team@7mind.io"),
)""".raw,
"scmInfo" in SettingScope.Build := """Some(ScmInfo(url("https://github.com/zio/izumi-reflect"), "scm:git:https://github.com/zio/izumi-reflect.git"))""".raw,
"mimaBinaryIssueFilters" in SettingScope.Build ++= Seq(
// ignore deletion of old ParsedLightTypeTag* classes, they were made package private more than a year ago, they shouldn't appear in library sources anymore - or in the first place
"""ProblemFilters.exclude[MissingClassProblem]("izumi.reflect.macrortti.LightTypeTag$ParsedLightTypeTag")""".raw,
"""ProblemFilters.exclude[MissingClassProblem]("izumi.reflect.macrortti.LightTypeTag$ParsedLightTypeTag110")""".raw,
"""ProblemFilters.exclude[MissingClassProblem]("izumi.reflect.macrortti.LightTypeTag$ParsedLightTypeTag210")""".raw,
"""ProblemFilters.exclude[MissingClassProblem]("izumi.reflect.macrortti.LightTypeTag$ParsedLightTypeTagM8")""".raw,
// dotty-only ProductX case class inheritance breakage
"""ProblemFilters.exclude[IncompatibleResultTypeProblem]("izumi.reflect.macrortti.LightTypeTagRef#FullReference._1")""".raw,
// new inherited methods added (2.11 problem only?)
"""ProblemFilters.exclude[InheritedNewAbstractMethodProblem]("izumi.reflect.macrortti.LightTypeTagRef*")""".raw,
// new methods added
"""ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LTTRenderables.r_LambdaParameterName")""".raw,
"""ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTag.binaryFormatVersion")""".raw,
"""ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagRef.repr")""".raw,
"""ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LTTRenderables.r_Wildcard")""".raw,
"""ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LTTRenderables.prefixSplitter")""".raw,
"""ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagRef.longNameWithPrefix")""".raw,
"""ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagRef.longNameInternalSymbol")""".raw,
"""ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagRef#AppliedNamedReference.symName")""".raw,
"""ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagRef#AppliedNamedReference.prefix")""".raw,
"""ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagRef.scalaStyledName")""".raw,
"""ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagRef.scalaStyledRepr")""".raw,
"""ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.AnyTag.=:=")""".raw,
"""ProblemFilters.exclude[ReversedMissingMethodProblem]("izumi.reflect.AnyTag.<:<")""".raw,
// compile-time only
"""ProblemFilters.exclude[Problem]("izumi.reflect.TagMacro.*")""".raw,
"""ProblemFilters.exclude[Problem]("izumi.reflect.macrortti.LightTypeTagImpl.*")""".raw,
"""ProblemFilters.exclude[Problem]("izumi.reflect.macrortti.LightTypeTagImpl#*")""".raw,
"""ProblemFilters.exclude[Problem]("izumi.reflect.dottyreflection.*")""".raw,
// private packages
"""ProblemFilters.exclude[Problem]("izumi.reflect.thirdparty.*")""".raw,
"""ProblemFilters.exclude[Problem]("izumi.reflect.internal.*")""".raw,
"""ProblemFilters.exclude[Problem]("izumi.reflect.ReflectionUtil*")""".raw,
// private methods
"""ProblemFilters.exclude[DirectMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagImpl.norm")""".raw,
"""ProblemFilters.exclude[DirectMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagImpl.izumi$reflect$macrortti$LightTypeTagImpl$$*")""".raw,
// private types
"""ProblemFilters.exclude[DirectMissingMethodProblem]("izumi.reflect.macrortti.LightTypeTagInheritance.CtxExt")""".raw,
"""ProblemFilters.exclude[MissingFieldProblem]("izumi.reflect.macrortti.LightTypeTagInheritance.CtxExt")""".raw,
"""ProblemFilters.exclude[FinalClassProblem]("izumi.reflect.macrortti.LightTypeTagInheritance$CtxExt")""".raw,
"""ProblemFilters.exclude[MissingTypesProblem] ("izumi.reflect.macrortti.LightTypeTagInheritance$Ctx*")""".raw,
"""ProblemFilters.exclude[Problem] ("izumi.reflect.macrortti.LightTypeTagInheritance#Ctx*")""".raw,
"""ProblemFilters.exclude[Problem] ("izumi.reflect.macrortti.LightTypeTagUnpacker*")""".raw
),
"mimaFailOnProblem" in SettingScope.Build := true,
"mimaFailOnNoPrevious" in SettingScope.Build := false,
"useGpg" in SettingScope.Build := false,
// scala-steward workaround
// add sbtgen version to sbt build to allow scala-steward to find it and update it in .sc files
// https://github.com/scala-steward-org/scala-steward/issues/696#issuecomment-545800968
"libraryDependencies" += s""""io.7mind.izumi.sbt" % "sbtgen_2.13" % "${Version.SbtGen.value}" % Provided""".raw
)
final val sharedSettings =
Defaults.CrossScalaPlusSources ++
Defaults.CrossScalaRangeSources ++
Seq(
"test" in Platform.Native := "{}".raw,
"test" in (SettingScope.Test, Platform.Native) := "{}".raw,
"sources" in SettingScope.Raw("Compile / doc") := Seq(
SettingKey(Some(scala300), None) := Seq.empty[String],
SettingKey.Default := "(Compile / doc / sources).value".raw
),
"testOptions" in SettingScope.Test += """Tests.Argument("-oDF")""".raw,
// "testOptions" in (SettingScope.Test, Platform.Jvm) ++= s"""Seq(Tests.Argument("-u"), Tests.Argument(s"$${target.value}/junit-xml-$${scalaVersion.value}"))""".raw,
"scalacOptions" ++= {
val removedOpts = Set[Const](
"-P:kind-projector:underscore-placeholders",
"-Vimplicits",
"-Xsource:3", // FIXME return after dropping 2.11 (NB: may cause bincompat issues)
"-Xsource:3-cross" // FIXME return after dropping 2.11 (NB: may cause bincompat issues)
)
val addedOpts = Seq[Const](
"-Wconf:msg=nowarn:silent"
)
Seq(
SettingKey(Some(scala211), None) := Const.EmptySeq,
SettingKey(Some(scala212), None) := Defaults.Scala212Options.filterNot(removedOpts) ++ addedOpts,
SettingKey(Some(scala213), None) := Defaults.Scala213Options.filterNot(removedOpts) ++ addedOpts,
SettingKey.Default := Seq(
"-Ykind-projector",
"-no-indent",
"-language:implicitConversions"
)
)
},
"scalacOptions" -= "-Wconf:any:error",
"mimaPreviousArtifacts" := Seq(
SettingKey(Some(scala300), None) := """Set(organization.value %% name.value % "2.2.5", organization.value %% name.value % "2.1.0")""".raw,
SettingKey.Default := """Set(organization.value %% name.value % "2.2.5", organization.value %% name.value % "2.1.0", organization.value %% name.value % "1.0.0")""".raw
),
"scalacOptions" ++= Seq(
SettingKey(Some(scala213), None) := Seq(
// workaround for https://github.com/scala/bug/issues/12103
"-Xlint:-implicit-recursion"
),
SettingKey.Default := Const.EmptySeq
),
"scalacOptions" ++= {
val inlineOpts = Seq(
"-opt:l:inline",
"-opt-inline-from:izumi.reflect.**"
)
Seq(
SettingKey(Some(scala212), Some(true)) := inlineOpts,
SettingKey(Some(scala213), Some(true)) := inlineOpts,
SettingKey.Default := Const.EmptySeq
)
},
// For compatibility with Java 9+ module system;
// without Automatic-Module-Name, the module name is derived from the jar file which is invalid because of the scalaVersion suffix.
"packageOptions" in SettingScope.Raw("Compile / packageBin") +=
s"""Package.ManifestAttributes("Automatic-Module-Name" -> s"$${organization.value.replaceAll("-",".")}.$${moduleName.value.replaceAll("-",".")}")""".raw
)
}
object izumi_reflect_aggregate {
final val id = ArtifactId("izumi-reflect-aggregate")
final val basePath = Seq("izumi-reflect")
final val izumi_reflect = ArtifactId("izumi-reflect")
final val thirdpartyBoopickleShaded = ArtifactId("izumi-reflect-thirdparty-boopickle-shaded")
}
}
final lazy val izumi_reflect_aggregate = Aggregate(
name = Projects.izumi_reflect_aggregate.id,
artifacts = Seq(
Artifact(
name = Projects.izumi_reflect_aggregate.thirdpartyBoopickleShaded,
libs = Seq.empty,
depends = Seq.empty,
settings = Seq(
SettingDef.RawSettingDef(
"""Compile / scalacOptions --= Seq("-Ywarn-value-discard","-Ywarn-unused:_", "-Wvalue-discard", "-Wunused:_")""",
FullSettingScope(SettingScope.Compile, Platform.All)
)
)
),
Artifact(
name = Projects.izumi_reflect_aggregate.izumi_reflect,
libs = Seq.empty,
depends = Seq(
Projects.izumi_reflect_aggregate.thirdpartyBoopickleShaded
)
)
),
pathPrefix = Projects.izumi_reflect_aggregate.basePath,
groups = Groups.izumi_reflect,
defaultPlatforms = Targets.crossNative,
enableProjectSharedAggSettings = false,
settings = Seq(
"crossScalaVersions" := "Nil".raw
)
)
final lazy val izumi_reflect: Project = Project(
name = Projects.root.id,
aggregates = Seq(
izumi_reflect_aggregate
),
topLevelSettings = Projects.root.settings,
sharedSettings = Projects.root.sharedSettings,
sharedAggSettings = Projects.root.sharedAggSettings,
rootSettings = Projects.root.rootSettings,
imports = Seq(
Import("com.typesafe.tools.mima.core._")
),
globalLibs = Seq(
ScopedLibrary(projector, FullDependencyScope(Scope.Compile, Platform.All).scalaVersion(ScalaVersionScope.AllScala2), compilerPlugin = true),
scala_reflect in Scope.Provided.all.scalaVersion(ScalaVersionScope.AllScala2),
scala3_compiler in Scope.Provided.all.scalaVersion(ScalaVersionScope.AllScala3),
scalatest in Scope.Test.all
),
rootPlugins = Projects.root.plugins,
globalPlugins = Plugins(),
pluginConflictRules = Map.empty,
appendPlugins = Defaults.SbtGenPlugins ++ Seq(
SbtPlugin("com.github.sbt", "sbt-pgp", PV.sbt_pgp),
SbtPlugin("org.scoverage", "sbt-scoverage", PV.sbt_scoverage),
SbtPlugin("com.typesafe", "sbt-mima-plugin", PV.sbt_mima_version),
SbtPlugin("dev.zio", "zio-sbt-website", PV.zio_sbt_website)
)
)
}
================================================
FILE: project/Settings.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import sbt.settingKey
object DocKeys {
lazy val prefix = settingKey[String]("")
}
================================================
FILE: project/Versions.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
object V {
val kind_projector = "0.13.3"
val scalatest = "3.2.19"
}
================================================
FILE: project/build.properties
================================================
#
# Copyright 2019-2020 Septimal Mind Ltd
# Copyright 2020 John A. De Goes and the ZIO Contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# You may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
sbt.version=1.11.2
================================================
FILE: project/plugins.sbt
================================================
// DO NOT EDIT THIS FILE
// IT IS AUTOGENERATED BY `sbtgen.sc` SCRIPT
// ALL CHANGES WILL BE LOST
// https://www.scala-js.org/
addSbtPlugin("org.scala-js" % "sbt-scalajs" % PV.scala_js_version)
// https://github.com/portable-scala/sbt-crossproject
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % PV.sbt_crossproject_version)
// https://scalacenter.github.io/scalajs-bundler/
addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.21.1")
// https://github.com/scala-js/jsdependencies
addSbtPlugin("org.scala-js" % "sbt-jsdependencies" % "1.0.2")
addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % PV.sbt_crossproject_version)
addSbtPlugin("org.scala-native" % "sbt-scala-native" % PV.scala_native_version)
////////////////////////////////////////////////////////////////////////////////
addSbtPlugin("io.7mind.izumi.sbt" % "sbt-izumi" % "0.0.107")
addSbtPlugin("com.github.sbt" % "sbt-pgp" % PV.sbt_pgp)
addSbtPlugin("org.scoverage" % "sbt-scoverage" % PV.sbt_scoverage)
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % PV.sbt_mima_version)
addSbtPlugin("dev.zio" % "zio-sbt-website" % PV.zio_sbt_website)
// Ignore scala-xml version conflict between scoverage where `coursier` requires scala-xml v2
// and scoverage requires scala-xml v1 on Scala 2.12,
// introduced when updating scoverage from 1.9.3 to 2.0.5
libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always
================================================
FILE: project/project/PluginVersions.scala
================================================
/*
* Copyright 2019-2020 Septimal Mind Ltd
* Copyright 2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
object PV {
val sbt_scoverage = "2.3.1"
val sbt_pgp = "2.3.1"
val sbt_mima_version = "1.1.0"
// last 2.11 version is 1.12.0, so we excluded 2.11 from JS builds, see nscplugin on maven central
val scala_js_version = "1.17.0"
// last 2.11 version is 0.4.9, so we excluded 2.11 from Native builds, see nscplugin on maven central
val scala_native_version = "0.5.7"
val sbt_crossproject_version = "1.3.2"
val zio_sbt_website = "0.4.0-alpha.31"
}
================================================
FILE: sbtgen.sc
================================================
#!/bin/sh
cs launch com.lihaoyi:ammonite_2.13.13:3.0.0-M1 --fork -M ammonite.Main -- sbtgen.sc $*
exit
!#
import $file.project.Deps, Deps._
@main
def entrypoint(args: String*) = Izumi.entrypoint(args)
================================================
FILE: version.sbt
================================================
ThisBuild / version := "3.0.10-SNAPSHOT"