blob: 9adb357e3309279c8bfb4cf007e169a0e26e6e2c [file] [log] [blame]
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// SharedOptions=--enable-experiment=constructor-tearoffs
// Tests that constructor tear-offs from type aliases work and
// are canonicalized correctly
import "package:expect/expect.dart";
import "../static_type_helper.dart";
void use(Type type) {}
class C<T> {
final T x;
C(this.x);
C.named(this.x);
}
typedef Special = C<int>; // Not generic. Not proper rename.
typedef Direct<T> = C<T>; // A proper rename
typedef Bounded<T extends num> = C<T>; // Not proper rename.
typedef Wrapping<T> = C<C<T>>; // Not proper rename.
typedef Extra<T, S> = C<T>; // Not proper rename.
class D<S, T> {
D();
D.named();
}
typedef Swapped<T, S> = D<S, T>; // Not a proper rename.
void main() {
// Tear-offs are constant if uninstantiated
// or if instantiated with a constant type.
const List<Object> constructors = [
Special.new,
Special.named,
Direct.new,
Direct.named,
Bounded.new,
Bounded.named,
Wrapping.new,
Wrapping.named,
Extra.new,
Extra.named,
Direct<int>.new,
Direct<int>.named,
Bounded<int>.new,
Bounded<int>.named,
Wrapping<int>.new,
Wrapping<int>.named,
Extra<int, int>.new,
Extra<int, int>.named,
const <C<int> Function(int)>[
Direct.new,
Direct.named,
Bounded.new,
Bounded.named,
],
const <C<C<int>> Function(C<int>)>[
Wrapping.new,
Wrapping.named,
],
const <C<int> Function(int)>[
Extra.new,
Extra.named,
]
];
Expect.isNotNull(constructors); // Use variable.
// The static type is as expected.
Special.new.expectStaticType<Exactly<C<int> Function(int)>>();
Special.named.expectStaticType<Exactly<C<int> Function(int)>>();
// Generic tear-off uses the generics of the type alias.
Direct.new.expectStaticType<Exactly<C<T> Function<T>(T)>>();
Direct.named.expectStaticType<Exactly<C<T> Function<T>(T)>>();
Bounded.new.expectStaticType<Exactly<C<T> Function<T extends num>(T)>>();
Bounded.named.expectStaticType<Exactly<C<T> Function<T extends num>(T)>>();
Wrapping.new.expectStaticType<Exactly<C<C<T>> Function<T>(C<T>)>>();
Wrapping.named.expectStaticType<Exactly<C<C<T>> Function<T>(C<T>)>>();
Extra.new.expectStaticType<Exactly<C<T> Function<T, S>(T)>>();
Extra.named.expectStaticType<Exactly<C<T> Function<T, S>(T)>>();
// Instantiated tear-off uses the instantiated aliased type.
// Explicitly instantiated.
Direct<int>.new.expectStaticType<Exactly<C<int> Function(int)>>();
Direct<int>.named.expectStaticType<Exactly<C<int> Function(int)>>();
Bounded<int>.new.expectStaticType<Exactly<C<int> Function(int)>>();
Bounded<int>.named.expectStaticType<Exactly<C<int> Function(int)>>();
Wrapping<int>.new.expectStaticType<Exactly<C<C<int>> Function(C<int>)>>();
Wrapping<int>.named.expectStaticType<Exactly<C<C<int>> Function(C<int>)>>();
Extra<int, String>.new.expectStaticType<Exactly<C<int> Function(int)>>();
Extra<int, String>.named.expectStaticType<Exactly<C<int> Function(int)>>();
// Implicitly instantiated.
context<C<int> Function(int)>(
Direct.new..expectStaticType<Exactly<C<int> Function(int)>>());
context<C<int> Function(int)>(
Direct.named..expectStaticType<Exactly<C<int> Function(int)>>());
context<C<int> Function(int)>(
Bounded.new..expectStaticType<Exactly<C<int> Function(int)>>());
context<C<int> Function(int)>(
Bounded.named..expectStaticType<Exactly<C<int> Function(int)>>());
context<C<C<int>> Function(C<int>)>(Wrapping.new
..expectStaticType<Exactly<C<C<int>> Function(C<int>)>>());
context<C<C<int>> Function(C<int>)>(Wrapping.named
..expectStaticType<Exactly<C<C<int>> Function(C<int>)>>());
context<C<int> Function(int)>(Extra.new
..expectStaticType<Exactly<C<int> Function(int)>>());
context<C<int> Function(int)>(Extra.named
..expectStaticType<Exactly<C<int> Function(int)>>());
// Uninstantiated tear-offs always canonicalize.
Expect.identical(Direct.new, Direct.new);
Expect.identical(Direct.named, Direct.named);
Expect.identical(Bounded.new, Bounded.new);
Expect.identical(Bounded.named, Bounded.named);
Expect.identical(Wrapping.new, Wrapping.new);
Expect.identical(Wrapping.named, Wrapping.named);
Expect.identical(Extra.new, Extra.new);
Expect.identical(Extra.named, Extra.named);
// Here the type is the same, and the alias is a proper rename.
Expect.identical(Direct.named, C.named);
Expect.identical(Direct.new, C.new);
// The next are unsurprising since the types are different.
Expect.notEquals(Bounded.named, C.named);
Expect.notEquals(Bounded.new, C.new);
Expect.notEquals(Wrapping.named, C.named);
Expect.notEquals(Wrapping.new, C.new);
Expect.notEquals(Extra.named, C.named);
Expect.notEquals(Extra.new, C.new);
Expect.notEquals(Swapped.new, D.new);
Expect.notEquals(Swapped.named, D.named);
// Instantiated alias tear-offs are canonicalized along with direct tear-offs
// when instantiation uses constant types.
// Explicit instantiation.
Expect.identical(Special.new, C<int>.new);
Expect.identical(Special.named, C<int>.named);
Expect.identical(Direct<int>.new, C<int>.new);
Expect.identical(Direct<int>.named, C<int>.named);
Expect.identical(Bounded<int>.new, C<int>.new);
Expect.identical(Bounded<int>.named, C<int>.named);
Expect.identical(Wrapping<int>.new, C<C<int>>.new);
Expect.identical(Wrapping<int>.named, C<C<int>>.named);
Expect.identical(Extra<int, String>.new, C<int>.new);
Expect.identical(Extra<int, String>.named, C<int>.named);
// And, since the second type parameter doesn't matter:
Expect.identical(Extra<int, String>.new, Extra<int, bool>.new);
Expect.identical(Extra<int, String>.named, Extra<int, bool>.named);
Expect.identical(Swapped<int, String>.new, D<String, int>.new);
Expect.identical(Swapped<int, String>.named, D<String, int>.named);
// Implicit instantiation.
Expect.identical(
context<C<int> Function(int)>(Direct.new),
C<int>.new,
);
Expect.identical(
context<C<int> Function(int)>(Direct.named),
C<int>.named,
);
Expect.identical(
context<C<int> Function(int)>(Bounded.new),
C<int>.new,
);
Expect.identical(
context<C<int> Function(int)>(Bounded.named),
C<int>.named,
);
Expect.identical(
context<C<C<int>> Function(C<int>)>(Wrapping.new),
C<C<int>>.new,
);
Expect.identical(
context<C<C<int>> Function(C<int>)>(Wrapping.named),
C<C<int>>.named,
);
Expect.identical(
context<D<int, String> Function()>(Swapped.new),
D<int, String>.new,
);
Expect.identical(
context<D<int, String> Function()>(Swapped.named),
D<int, String>.named,
);
(<T extends num>() {
// Non-constant type instantiation implies non-constant expressions.
// Canonicalization is unspecified, but equality holds.
Expect.equals(Direct<T>.new, Direct<T>.new);
Expect.equals(Direct<T>.named, Direct<T>.named);
Expect.equals(Bounded<T>.new, Bounded<T>.new);
Expect.equals(Bounded<T>.named, Bounded<T>.named);
Expect.equals(Wrapping<T>.new, Wrapping<T>.new);
Expect.equals(Wrapping<T>.named, Wrapping<T>.named);
Expect.equals(Extra<T, String>.new, Extra<T, String>.new);
Expect.equals(Extra<T, String>.named, Extra<T, String>.named);
// Also if the non-constant type doesn't occur in the expansion.
Expect.equals(Extra<int, T>.new, Extra<int, T>.new);
Expect.equals(Extra<int, T>.named, Extra<int, T>.named);
Expect.equals(Swapped<T, int>.new, Swapped<T, int>.new);
Expect.equals(Swapped<T, int>.named, Swapped<T, int>.named);
Expect.equals(Swapped<int, T>.new, Swapped<int, T>.new);
Expect.equals(Swapped<int, T>.named, Swapped<int, T>.named);
}<int>());
}